// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * ip_vs_xmit.c: various packet transmitters for IPVS
 *
 * Authors:     Wensong Zhang <wensong@linuxvirtualserver.org>
 *              Julian Anastasov <ja@ssi.bg>
 *
 * Changes:
 *
 * Description of forwarding methods:
 * - all transmitters are called from LOCAL_IN (remote clients) and
 * LOCAL_OUT (local clients) but for ICMP can be called from FORWARD
 * - not all connections have destination server, for example,
 * connections in backup server when fwmark is used
 * - bypass connections use daddr from packet
 * - we can use dst without ref while sending in RCU section, we use
 * ref when returning NF_ACCEPT for NAT-ed packet via loopback
 * LOCAL_OUT rules:
 * - skb->dev is NULL, skb->protocol is not set (both are set in POST_ROUTING)
 * - skb->pkt_type is not set yet
 * - the only place where we can see skb->sk != NULL
 */

#define KMSG_COMPONENT "IPVS"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt

#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/tcp.h>                  /* for tcphdr */
#include <net/ip.h>
#include <net/gue.h>
#include <net/gre.h>
#include <net/tcp.h>                    /* for csum_tcpudp_magic */
#include <net/udp.h>
#include <net/icmp.h>                   /* for icmp_send */
#include <net/route.h>                  /* for ip_route_output */
#include <net/ipv6.h>
#include <net/ip6_route.h>
#include <net/ip_tunnels.h>
#include <net/ip6_checksum.h>
#include <net/addrconf.h>
#include <linux/icmpv6.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h>

#include <net/udp.h>
#include <net/ip6_checksum.h>

#include "./ip_vs_fnat.h"

enum {
	IP_VS_RT_MODE_LOCAL	= 1, /* Allow local dest */
	IP_VS_RT_MODE_NON_LOCAL	= 2, /* Allow non-local dest */
	IP_VS_RT_MODE_RDR	= 4, /* Allow redirect from remote daddr to
				      * local
				      */
	IP_VS_RT_MODE_CONNECT	= 8, /* Always bind route to saddr */
	IP_VS_RT_MODE_KNOWN_NH	= 16,/* Route via remote addr */
	IP_VS_RT_MODE_TUNNEL	= 32,/* Tunnel mode */
};

#define VXLAN_HF_ELB cpu_to_be32(BIT(30))
#define VXLAN_LVS_INDEX_SHIFT 8
#define VXLAN_VNI_SHIT 8
#define VXLAN_IPHDR_OSFFSET (sizeof(struct vxlanhdr) + sizeof(struct udphdr) + sizeof(struct iphdr))
#define VXLAN_IPV6HDR_OSFFSET (sizeof(struct vxlanhdr) + sizeof(struct udphdr) + sizeof(struct ipv6hdr))

static inline struct ip_vs_dest_dst *ip_vs_dest_dst_alloc(void)
{
	return kmalloc(sizeof(struct ip_vs_dest_dst), GFP_ATOMIC);
}

static inline void ip_vs_dest_dst_free(struct ip_vs_dest_dst *dest_dst)
{
	kfree(dest_dst);
}

/*
 *      Destination cache to speed up outgoing route lookup
 */
static inline void
__ip_vs_dst_set(struct ip_vs_dest *dest, struct ip_vs_dest_dst *dest_dst,
		struct dst_entry *dst, u32 dst_cookie)
{
	struct ip_vs_dest_dst *old;

	old = rcu_dereference_protected(dest->dest_dst,
					lockdep_is_held(&dest->dst_lock));

	if (dest_dst) {
		dest_dst->dst_cache = dst;
		dest_dst->dst_cookie = dst_cookie;
	}
	rcu_assign_pointer(dest->dest_dst, dest_dst);

	if (old)
		call_rcu(&old->rcu_head, ip_vs_dest_dst_rcu_free);
}

static inline struct ip_vs_dest_dst *
__ip_vs_dst_check(struct ip_vs_dest *dest)
{
	struct ip_vs_dest_dst *dest_dst = rcu_dereference(dest->dest_dst);
	struct dst_entry *dst;

	if (!dest_dst)
		return NULL;
	dst = dest_dst->dst_cache;
	if (dst->obsolete &&
	    dst->ops->check(dst, dest_dst->dst_cookie) == NULL)
		return NULL;
	return dest_dst;
}

/* Get route to daddr, update *saddr, optionally bind route to saddr */
static struct rtable *do_output_route4(struct net *net, __be32 daddr,
				       int rt_mode, __be32 *saddr, __u32 devid)
{
	struct flowi4 fl4;
	struct rtable *rt;
	bool loop = false;
	__be32 srcaddr = 0;

	if (saddr != NULL &&
			(local_vtep.src_sen_routing ||
			 (rt_mode & IP_VS_RT_MODE_CONNECT)))
		srcaddr = *saddr;

	memset(&fl4, 0, sizeof(fl4));
	fl4.daddr = daddr;
	fl4.saddr = srcaddr;
	fl4.flowi4_flags = (rt_mode & IP_VS_RT_MODE_KNOWN_NH) ?
			   FLOWI_FLAG_KNOWN_NH : 0;
	fl4.flowi4_oif = devid;

retry:
	rt = ip_route_output_key(net, &fl4);
	if (IS_ERR(rt)) {
		/* Invalid saddr ? */
		if (PTR_ERR(rt) == -EINVAL && *saddr &&
		    rt_mode & IP_VS_RT_MODE_CONNECT && !loop) {
			*saddr = 0;
			flowi4_update_output(&fl4, 0, 0, daddr, 0);
			goto retry;
		}
		IP_VS_DBG_RL("ip_route_output error, dest: %pI4\n", &daddr);
		return NULL;
	} else if (!*saddr && rt_mode & IP_VS_RT_MODE_CONNECT && fl4.saddr) {
		ip_rt_put(rt);
		*saddr = fl4.saddr;
		flowi4_update_output(&fl4, 0, 0, daddr, fl4.saddr);
		loop = true;
		goto retry;
	}
	*saddr = fl4.saddr;
	return rt;
}

#ifdef CONFIG_IP_VS_IPV6
static inline int __ip_vs_is_local_route6(struct rt6_info *rt)
{
	return rt->dst.dev && rt->dst.dev->flags & IFF_LOOPBACK;
}
#endif

static inline bool crosses_local_route_boundary(int skb_af, struct sk_buff *skb,
						int rt_mode,
						bool new_rt_is_local)
{
	bool rt_mode_allow_local = !!(rt_mode & IP_VS_RT_MODE_LOCAL);
	bool rt_mode_allow_non_local = !!(rt_mode & IP_VS_RT_MODE_NON_LOCAL);
	bool rt_mode_allow_redirect = !!(rt_mode & IP_VS_RT_MODE_RDR);
	bool source_is_loopback;
	bool old_rt_is_local;

	if (skb_dst(skb) == NULL)
		return false;
#ifdef CONFIG_IP_VS_IPV6
	if (skb_af == AF_INET6) {
		int addr_type = ipv6_addr_type(&ipv6_hdr(skb)->saddr);

		source_is_loopback =
			(!skb->dev || skb->dev->flags & IFF_LOOPBACK) &&
			(addr_type & IPV6_ADDR_LOOPBACK);
		old_rt_is_local = __ip_vs_is_local_route6(
			(struct rt6_info *)skb_dst(skb));
	} else
#endif
	{
		source_is_loopback = ipv4_is_loopback(ip_hdr(skb)->saddr);
		old_rt_is_local = skb_rtable(skb)->rt_flags & RTCF_LOCAL;
	}

	if (unlikely(new_rt_is_local)) {
		if (!rt_mode_allow_local)
			return true;
		if (!rt_mode_allow_redirect && !old_rt_is_local)
			return true;
	} else {
		if (!rt_mode_allow_non_local)
			return true;
		if (source_is_loopback)
			return true;
	}
	return false;
}

static inline void maybe_update_pmtu(int skb_af, struct sk_buff *skb, int mtu)
{
	struct sock *sk = skb->sk;
	struct rtable *ort = skb_rtable(skb);

	if (!skb->dev && sk && sk_fullsock(sk))
		ort->dst.ops->update_pmtu(&ort->dst, sk, NULL, mtu, true);
}

static inline bool decrement_ttl(struct netns_ipvs *ipvs,
				 int skb_af,
				 struct sk_buff *skb)
{
	struct net *net = ipvs->net;

#ifdef CONFIG_IP_VS_IPV6
	if (skb_af == AF_INET6) {
		/* check and decrement ttl */
		if (ipv6_hdr(skb)->hop_limit <= 1) {
			struct inet6_dev *idev = __in6_dev_get_safely(skb->dev);

			icmpv6_send(skb, ICMPV6_TIME_EXCEED,
				    ICMPV6_EXC_HOPLIMIT, 0);
			__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);

			return false;
		}

		/* don't propagate ttl change to cloned packets */
		if (skb_ensure_writable(skb, sizeof(struct ipv6hdr)))
			return false;

		ipv6_hdr(skb)->hop_limit--;
	} else
#endif
	{
		if (ip_hdr(skb)->ttl <= 1) {
			/* Tell the sender its packet died... */
			__IP_INC_STATS(net, IPSTATS_MIB_INHDRERRORS);
			icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0);
			return false;
		}

		/* don't propagate ttl change to cloned packets */
		if (skb_ensure_writable(skb, sizeof(struct iphdr)))
			return false;

		/* Decrease ttl */
		ip_decrease_ttl(ip_hdr(skb));
	}

	return true;
}

/* Get route to destination or remote server */
static int
__ip_vs_get_out_rt(struct netns_ipvs *ipvs, struct sk_buff *skb,
		   struct ip_vs_conn *cp,
		   __be32 daddr, int rt_mode, __be32 *ret_saddr,
		   struct ip_vs_iphdr *ipvsh)
{
	struct net *net = cp->ipvs->net;
	struct ip_vs_dest_dst *dest_dst;
	struct rtable *rt;			/* Route to the other host */
	int mtu;
	int local, noref = 1;
	__u32 vni = 0;
	__be32 src, dst;
	struct ip_vs_dest *dest = cp->dest;
	int skb_af = cp->af;

	if ((dest != NULL) &&
		(local_vtep.network_type != IP_VS_NETWORK_VLAN))
		vni = dest->vni.out_vni;

	if (dest) {
		dest_dst = __ip_vs_dst_check(dest);
		if (likely(dest_dst)) {
			rt = (struct rtable *) dest_dst->dst_cache;
			if (vni == 0) {
				src = dest_dst->dst_saddr.ip;
				dst = dest->addr.ip;
			} else {
				src = local_vtep.vtepaddr.ip;
				dst = dest->vni.vtepaddr.ip;
			}
		} else {
			dest_dst = ip_vs_dest_dst_alloc();
			spin_lock_bh(&dest->dst_lock);
			if (!dest_dst) {
				__ip_vs_dst_set(dest, NULL, NULL, 0);
				spin_unlock_bh(&dest->dst_lock);
				goto err_unreach;
			}
			if (vni != 0) {
				src = local_vtep.vtepaddr.ip;
				dst = dest->vni.vtepaddr.ip;
			} else {
				src = 0;
				dst = dest->addr.ip;
			}

			rt = do_output_route4(net, dst, rt_mode, &src, dest->oif_id);
			if (!rt) {
				__ip_vs_dst_set(dest, NULL, NULL, 0);
				spin_unlock_bh(&dest->dst_lock);
				ip_vs_dest_dst_free(dest_dst);
				goto err_unreach;
			}
			dest_dst->dst_saddr.ip = src;
			__ip_vs_dst_set(dest, dest_dst, &rt->dst, 0);
			spin_unlock_bh(&dest->dst_lock);
			IP_VS_DBG(10, "new dst %pI4, src %pI4, refcnt=%d\n",
				  &dst, &dest_dst->dst_saddr.ip,
				  atomic_read(&rt->dst.__refcnt));
		}
		daddr = dst;
		if (ret_saddr)
			*ret_saddr = src;
	} else {
		__be32 saddr = htonl(INADDR_ANY);

		noref = 0;

		/* For such unconfigured boxes avoid many route lookups
		 * for performance reasons because we do not remember saddr
		 */
		rt_mode &= ~IP_VS_RT_MODE_CONNECT;
		rt = do_output_route4(net, daddr, rt_mode, &saddr, 0);
		if (!rt)
			goto err_unreach;
		if (ret_saddr)
			*ret_saddr = saddr;
	}

	local = (rt->rt_flags & RTCF_LOCAL) ? 1 : 0;
	if (unlikely(crosses_local_route_boundary(skb_af, skb, rt_mode,
						  local))) {
		IP_VS_DBG_RL("We are crossing local and non-local addresses"
			     " daddr=%pI4\n", &daddr);
		goto err_put;
	}

	if (unlikely(local)) {
		/* skb to local stack, preserve old route */
		if (!noref)
			ip_rt_put(rt);
		return -1;
	}

	if (!decrement_ttl(ipvs, skb_af, skb))
		goto err_put;

	if (likely(!(rt_mode & IP_VS_RT_MODE_TUNNEL))) {
		mtu = dst_mtu(&rt->dst);
	} else {
		mtu = dst_mtu(&rt->dst) - sizeof(struct iphdr);
		if (!dest)
			goto err_put;
		if (dest->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GUE) {
			mtu -= sizeof(struct udphdr) + sizeof(struct guehdr);
			if ((dest->tun_flags &
			     IP_VS_TUNNEL_ENCAP_FLAG_REMCSUM) &&
			    skb->ip_summed == CHECKSUM_PARTIAL)
				mtu -= GUE_PLEN_REMCSUM + GUE_LEN_PRIV;
		} else if (dest->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE) {
			__be16 tflags = 0;

			if (dest->tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_CSUM)
				tflags |= TUNNEL_CSUM;
			mtu -= gre_calc_hlen(tflags);
		}
		if (mtu < 68) {
			IP_VS_DBG_RL("%s(): mtu less than 68\n", __func__);
			goto err_put;
		}
		maybe_update_pmtu(skb_af, skb, mtu);
	}

	skb_dst_drop(skb);
	if (noref)
		skb_dst_set_noref(skb, &rt->dst);
	else
		skb_dst_set(skb, &rt->dst);

	return local;

err_put:
	if (!noref)
		ip_rt_put(rt);
	return -1;

err_unreach:
	dst_link_failure(skb);
	return -1;
}

/* Get route to daddr, update *saddr, optionally bind route to saddr */
static struct rtable *__ip_vs_get_rt(struct net *net, __be32 daddr,
				       int rt_mode, __be32 *saddr, __u32 devid)
{
	__be32 _saddr = 0;
	struct rtable *rt = NULL;

	if (saddr != NULL)
		_saddr = *saddr;
	rt = do_output_route4(net, daddr, rt_mode, &_saddr, devid);
	if (rt && saddr)
		*saddr = _saddr;
	return rt;
}

#ifdef CONFIG_IP_VS_IPV6
static struct dst_entry *
__ip_vs_route_output_v6(struct net *net, struct in6_addr *daddr,
			struct in6_addr *ret_saddr, int do_xfrm, int rt_mode, __u32 devid)
{
	struct dst_entry *dst = NULL;
	struct flowi6 fl6;
	struct in6_addr srcaddr;

	memset(&srcaddr, 0, sizeof(srcaddr));
	if (local_vtep.src_sen_routing && ret_saddr != NULL)
		memcpy(&srcaddr, ret_saddr, sizeof(srcaddr));

	memset(&fl6, 0, sizeof(fl6));
	fl6.saddr = srcaddr;
	fl6.daddr = *daddr;
	if (rt_mode & IP_VS_RT_MODE_KNOWN_NH)
		fl6.flowi6_flags = FLOWI_FLAG_KNOWN_NH;
	fl6.flowi6_oif = devid;

	dst = ip6_route_output(net, NULL, &fl6);
	if (dst->error)
		goto out_err;
	if (!ret_saddr)
		return dst;
	if (ipv6_addr_any(&fl6.saddr) &&
	    ipv6_dev_get_saddr(net, ip6_dst_idev(dst)->dev,
			       &fl6.daddr, 0, &fl6.saddr) < 0)
		goto out_err;
	if (do_xfrm) {
		dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
		if (IS_ERR(dst)) {
			dst = NULL;
			goto out_err;
		}
	}
	*ret_saddr = fl6.saddr;
	return dst;

out_err:
	dst_release(dst);
	IP_VS_DBG_RL("ip6_route_output error, dest: %pI6\n", daddr);
	return NULL;
}

/*
 * Get route to destination or remote server
 */
static int
__ip_vs_get_out_rt_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
		      struct ip_vs_conn *cp,
		      struct in6_addr *daddr, struct in6_addr *ret_saddr,
		      struct ip_vs_iphdr *ipvsh, int do_xfrm, int rt_mode)
{
	struct net *net = cp->ipvs->net;
	struct ip_vs_dest_dst *dest_dst;
	struct rt6_info *rt;			/* Route to the other host */
	struct dst_entry *dst;
	int mtu;
	int local, noref = 1;
	__u32 vni = 0;
	struct in6_addr srcaddr, dstaddr;
	struct ip_vs_dest *dest = cp->dest;
	int skb_af = cp->af;

	if ((dest != NULL) &&
		 (local_vtep.network_type != IP_VS_NETWORK_VLAN))
		vni = dest->vni.out_vni;

	if (dest) {
		dest_dst = __ip_vs_dst_check(dest);
		if (likely(dest_dst)) {
			rt = (struct rt6_info *) dest_dst->dst_cache;
			if (vni == 0) {
				srcaddr = dest_dst->dst_saddr.in6;
				dstaddr = dest->addr.in6;
			} else {
				srcaddr = local_vtep.vtepaddr.in6;
				dstaddr = dest->vni.vtepaddr.in6;
			}
		} else {
			u32 cookie;

			dest_dst = ip_vs_dest_dst_alloc();
			spin_lock_bh(&dest->dst_lock);
			if (!dest_dst) {
				__ip_vs_dst_set(dest, NULL, NULL, 0);
				spin_unlock_bh(&dest->dst_lock);
				goto err_unreach;
			}
			if (vni != 0) {
				srcaddr = local_vtep.vtepaddr.in6;
				dstaddr = dest->vni.vtepaddr.in6;
			} else {
				memset(&srcaddr, 0, sizeof(struct in6_addr));
				dstaddr = dest->addr.in6;
			}

			dst = __ip_vs_route_output_v6(net, &dstaddr,
						      &srcaddr, do_xfrm, rt_mode, dest->oif_id);
			if (!dst) {
				__ip_vs_dst_set(dest, NULL, NULL, 0);
				spin_unlock_bh(&dest->dst_lock);
				ip_vs_dest_dst_free(dest_dst);
				goto err_unreach;
			}
			dest_dst->dst_saddr.in6 = srcaddr;
			rt = (struct rt6_info *) dst;
			cookie = rt6_get_cookie(rt);
			__ip_vs_dst_set(dest, dest_dst, &rt->dst, cookie);
			spin_unlock_bh(&dest->dst_lock);
			IP_VS_DBG(10, "new dst %pI6, src %pI6, refcnt=%d\n",
				  &dstaddr, &dest_dst->dst_saddr.in6,
				  atomic_read(&rt->dst.__refcnt));
		}
		if (ret_saddr)
			*ret_saddr = srcaddr;
	} else {
		noref = 0;
		dst = __ip_vs_route_output_v6(net, daddr, ret_saddr,
					do_xfrm, rt_mode, 0);
		if (!dst)
			goto err_unreach;
		rt = (struct rt6_info *) dst;
	}

	local = __ip_vs_is_local_route6(rt);

	if (unlikely(crosses_local_route_boundary(skb_af, skb, rt_mode,
						  local))) {
		IP_VS_DBG_RL("We are crossing local and non-local addresses"
			     " daddr=%pI6\n", daddr);
		goto err_put;
	}

	if (unlikely(local)) {
		/* skb to local stack, preserve old route */
		if (!noref)
			dst_release(&rt->dst);
		return -1;
	}

	if (!decrement_ttl(ipvs, skb_af, skb))
		goto err_put;

	/* MTU checking */
	if (unlikely(rt_mode & IP_VS_RT_MODE_TUNNEL)) {
		mtu = dst_mtu(&rt->dst) - sizeof(struct ipv6hdr);
		if (!dest)
			goto err_put;
		if (dest->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GUE) {
			mtu -= sizeof(struct udphdr) + sizeof(struct guehdr);
			if ((dest->tun_flags &
			     IP_VS_TUNNEL_ENCAP_FLAG_REMCSUM) &&
			    skb->ip_summed == CHECKSUM_PARTIAL)
				mtu -= GUE_PLEN_REMCSUM + GUE_LEN_PRIV;
		} else if (dest->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE) {
			__be16 tflags = 0;

			if (dest->tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_CSUM)
				tflags |= TUNNEL_CSUM;
			mtu -= gre_calc_hlen(tflags);
		}
		if (mtu < IPV6_MIN_MTU) {
			IP_VS_DBG_RL("%s(): mtu less than %d\n", __func__,
				     IPV6_MIN_MTU);
			goto err_put;
		}
		maybe_update_pmtu(skb_af, skb, mtu);
	}

	skb_dst_drop(skb);
	if (noref)
		skb_dst_set_noref(skb, &rt->dst);
	else
		skb_dst_set(skb, &rt->dst);

	return local;

err_put:
	if (!noref)
		dst_release(&rt->dst);
	return -1;

err_unreach:
	dst_link_failure(skb);
	return -1;
}

static struct dst_entry *
__ip_vs_get_rt_v6(struct net *net, struct in6_addr *daddr,
			struct in6_addr *ret_saddr, int do_xfrm, int rt_mode, __u32 devid)
{
	struct in6_addr ret_src;
	struct dst_entry *dst = NULL;

	if (ret_saddr != NULL)
		memcpy(&ret_src, ret_saddr, sizeof(struct in6_addr));
	dst = __ip_vs_route_output_v6(net, daddr, &ret_src, do_xfrm, rt_mode, devid);
	if (dst && ret_saddr)
		*ret_saddr = ret_src;

	return dst;
}
#endif

static int __ip_vs_get_vtep_dst(struct sk_buff *skb, struct ip_vs_conn *cp,
				union nf_inet_addr *ret_saddr, struct ip_vs_iphdr *ipvsh,
				int do_xfrm, int rt_mode)
{
	int local = 0;

	if (local_vtep.af == AF_INET) {
		local = __ip_vs_get_out_rt(cp->ipvs, skb, cp, cp->daddr.ip, rt_mode,
			(ret_saddr == NULL) ? NULL : &ret_saddr->ip, ipvsh);
	} else {
		local = __ip_vs_get_out_rt_v6(cp->ipvs, skb, cp, &cp->daddr.in6,
				(ret_saddr == NULL) ? NULL : &ret_saddr->in6,
				ipvsh, do_xfrm, rt_mode);
	}

	return local;
}

#define IP_VS_XMIT(pf, skb, rt, net)			\
do {							\
	(skb)->ipvs_property = 1;			\
	skb_forward_csum(skb);				\
	NF_HOOK(pf, NF_INET_LOCAL_OUT, net,		\
		NULL, (skb), NULL,			\
		(rt)->dst.dev, dst_output);		\
} while (0)

/* check if gso can handle the skb */
int gso_ok(struct sk_buff *skb, struct net_device *dev)
{
	netdev_features_t  features = dev->features;

	if (skb_is_gso(skb)) {
		/* LRO check */
		if (unlikely(skb_shinfo(skb)->gso_type == 0)) {
			IP_VS_ERR_RL("%s:LRO is enabled."
					"Cannot be forwarded\n", dev->name);
			IP_VS_INC_ESTATS(ip_vs_esmib, LRO_REJECT);
			goto gso_err;
		}

		/* GRO check */
		if (net_gso_ok(features|NETIF_F_GSO_ROBUST, skb_shinfo(skb)->gso_type)) {
			/* the skb has frag_list, need do sth here */
			if (skb_has_frag_list(skb) &&
					!(dev->features & NETIF_F_FRAGLIST) &&
							__skb_linearize(skb))
				goto gso_err;

			IP_VS_DBG_RL("skb length: %d . GSO is ok."
					"can be forwarded\n", skb->len);
			IP_VS_INC_ESTATS(ip_vs_esmib, GRO_PASS);
			goto gso_ok;
		}
	}

gso_err:
	return 0;
gso_ok:
	return 1;
}

#ifdef CONFIG_IP_VS_IPV6
int ip_vs_fnat_upstream_fast_xmit_v6(struct sk_buff *skb,
				struct ipvs_vxlan_encapsulation_info *encap_info)
{
	struct ethhdr *eth_h;
	skb->dev = encap_info->indev;

	if (unlikely(skb_headroom(skb) < LL_RESERVED_SPACE(skb->dev))) {
		struct sk_buff *skb_tmp;

		IP_VS_ERR_RL("need more headroom! realloc skb\n");
		skb_tmp = skb_realloc_headroom(skb, LL_RESERVED_SPACE(skb->dev));
		if (skb_tmp == NULL) {
			kfree_skb(skb);
			goto err_out;
		}
		kfree_skb(skb);
		skb = skb_tmp;
	}

	eth_h = (struct ethhdr *)skb_push(skb, sizeof(struct ethhdr));
	skb_reset_mac_header(skb);
	memcpy(eth_h->h_dest, encap_info->peer_vtep->out_src_mac, ETH_ALEN);
	memcpy(eth_h->h_source, encap_info->peer_vtep->out_dst_mac, ETH_ALEN);
	if (local_vtep.af == AF_INET6) {
		skb->protocol = eth_h->h_proto = htons(ETH_P_IPV6);
	} else {
		skb->protocol = eth_h->h_proto = htons(ETH_P_IP);
	}
	skb->pkt_type = PACKET_OUTGOING;

	IP_VS_DBG_RL("%s: send skb to client!\n", __func__);
	/* Send the packet out */
	do {
		int ret;
		ret = dev_queue_xmit(skb);
		if (ret != 0)
			IP_VS_ERR_RL("dev_queue_xmit failed! code:%d\n", ret);
	} while (0);

	IP_VS_INC_ESTATS(ip_vs_esmib, FAST_XMIT_PASS);
	return 0;
err_out:
	IP_VS_INC_ESTATS(ip_vs_esmib, FAST_XMIT_REJECT);
	return 1;
}
#endif

static void ip_vs_udp_set_csum(struct sk_buff *skb,
			       union nf_inet_addr *src_vtepaddr,
			       union nf_inet_addr *dst_vtepaddr)
{
	struct udphdr *uh = udp_hdr(skb);

	if (skb_is_gso(skb)) {
		if (local_vtep.af == AF_INET) {
			uh->check = ~udp_v4_check(skb->len, src_vtepaddr->ip,
						  dst_vtepaddr->ip, 0);
		} else {
			uh->check = ~udp_v6_check(skb->len,
						  (const struct in6_addr *)&(src_vtepaddr->in6),
						  (const struct in6_addr *)&(dst_vtepaddr->in6), 0);
		}
	} else if (skb_dst(skb) && skb_dst(skb)->dev &&
		   (skb_dst(skb)->dev->features & NETIF_F_IP_CSUM)) {
		skb->ip_summed = CHECKSUM_PARTIAL;
		skb->csum_start = skb_transport_header(skb) - skb->head;
		skb->csum_offset = offsetof(struct udphdr, check);
		if (local_vtep.af == AF_INET) {
			uh->check = ~udp_v4_check(skb->len, src_vtepaddr->ip,
						  dst_vtepaddr->ip, 0);
		} else {
			uh->check = ~udp_v6_check(skb->len,
						  (const struct in6_addr *)&(src_vtepaddr->in6),
						  (const struct in6_addr *)&(dst_vtepaddr->in6), 0);
		}
	} else {
		__wsum csum;
		uh->check = 0;
		csum = skb_checksum(skb, 0, skb->len, 0);
		if (local_vtep.af == AF_INET) {
			uh->check = udp_v4_check(skb->len, src_vtepaddr->ip,
						 dst_vtepaddr->ip, csum);
		} else {
			uh->check = udp_v6_check(skb->len,
						 (const struct in6_addr *)&(src_vtepaddr->in6),
						 (const struct in6_addr *)&(dst_vtepaddr->in6), csum);
		}
		if (uh->check == 0)
			uh->check = CSUM_MANGLED_0;
		skb->ip_summed = CHECKSUM_UNNECESSARY;
	}
}

/*enpacket vxlan head*/
int vxlan_encapsulation(struct sk_buff *skb_e,
			struct ipvs_vxlan_encapsulation_info *encap_info,
			int (*output)(struct sock *, struct sk_buff *))
{
	struct ipv6hdr *ipv6h_out = NULL;
	struct iphdr *iph_out = NULL;
	struct ethhdr *eth_h = NULL;
	struct vxlanhdr *vxh = NULL;
	struct udphdr *uh = NULL;
	struct dst_entry *dst = NULL;
	struct net *net = NULL;
	struct netns_ipvs *ipvs = NULL;
	union nf_inet_addr *src_vtepaddr = NULL, *dst_vtepaddr = NULL;
	struct ip_vs_vni *vni = encap_info->vni;
	struct ip_vs_peer_info *peer_vtep = encap_info->peer_vtep;
	__u8 ttl;
	__u8 iphdrlen;
	__be16 outer_proto;
	unsigned int headroom = 0;

	EnterFunction(10);

	dst = skb_dst(skb_e);

	if (local_vtep.af == AF_INET) {
		iphdrlen = sizeof(struct iphdr);
		outer_proto = ETH_P_IP;
	} else {
		iphdrlen = sizeof(struct ipv6hdr);
		outer_proto = ETH_P_IPV6;
	}

	headroom = LL_RESERVED_SPACE(dst->dev) + iphdrlen +
		   sizeof(struct udphdr) + sizeof(struct vxlanhdr) +
		   sizeof(struct ethhdr);

	/* Be paranoid, rather than too clever. */
	if (unlikely(skb_headroom(skb_e) < headroom)) {
		struct sk_buff *skb2;

		skb2 = skb_realloc_headroom(skb_e, headroom);
		if (skb2 == NULL) {
			IP_VS_ERR_RL("%s(): no memory\n", __func__);
			kfree_skb(skb_e);
			return -ENOMEM;
		}
		kfree_skb(skb_e);
		skb_e = skb2;
	}
	skb_push(skb_e, sizeof(*eth_h));
	skb_reset_mac_header(skb_e);
	eth_h = (struct ethhdr *)skb_mac_header(skb_e);
	if (peer_vtep == NULL) {
		ether_addr_copy(eth_h->h_dest, vni->mac);
		memcpy(eth_h->h_source, dst->dev->dev_addr, dst->dev->addr_len);
	} else {
		ether_addr_copy(eth_h->h_dest, encap_info->inner_src_mac);
		ether_addr_copy(eth_h->h_source, encap_info->inner_dst_mac);
	}

	if (skb_e->protocol == htons(ETH_P_IP)) {
		eth_h->h_proto = htons(ETH_P_IP);
		ttl = ip_hdr(skb_e)->ttl;
	} else {
		eth_h->h_proto = htons(ETH_P_IPV6);
		ttl = ipv6_hdr(skb_e)->hop_limit;
	}

	vxh = (struct vxlanhdr *) __skb_push(skb_e, sizeof(*vxh));
	vxh->vx_flags = VXLAN_HF_VNI;
	if (peer_vtep == NULL) {
		if (likely(!vni->vxlan_private_flag_disable))
			vxh->vx_flags |= (VXLAN_HF_ELB |
					  htonl(local_vtep.lvs_index <<
						VXLAN_LVS_INDEX_SHIFT));
		vxh->vx_vni = htonl(vni->out_vni << VXLAN_VNI_SHIT);
	} else {
		vxh->vx_vni = htonl(peer_vtep->vni << VXLAN_VNI_SHIT);
	}

	skb_push(skb_e, sizeof(*uh));
	skb_reset_transport_header(skb_e);
	uh = udp_hdr(skb_e);
	if (peer_vtep == NULL) {
		uh->dest = vni->vtepport;
		uh->source = vni->sport;
		dst_vtepaddr = &(vni->vtepaddr);
	} else {
		uh->dest = htons(VXLAN_UDP_PORT);
		uh->source = encap_info->dport;
		dst_vtepaddr = &(peer_vtep->vtepaddr);
	}
	src_vtepaddr = &(local_vtep.vtepaddr);
	uh->len = htons(skb_e->len);

	ip_vs_udp_set_csum(skb_e, src_vtepaddr, dst_vtepaddr);
	/* Push down and install the network header. */
	skb_push(skb_e, iphdrlen);
	skb_reset_network_header(skb_e);

	if (local_vtep.af == AF_INET) {
		iph_out = ip_hdr(skb_e);
		iph_out->version        =       4;
		iph_out->ihl            =       sizeof(struct iphdr) >> 2;
		iph_out->frag_off       =       htons(IP_DF);
		iph_out->protocol       =       IPPROTO_UDP;
		iph_out->tos            =       0;
		iph_out->daddr          =       dst_vtepaddr->ip;
		iph_out->saddr          =       src_vtepaddr->ip;
		iph_out->ttl            =       ttl;
		iph_out->tot_len        =       htons(skb_e->len);
		ip_send_check(iph_out);
	} else {
		ipv6h_out = ipv6_hdr(skb_e);
		ip6_flow_hdr(ipv6h_out, 0, 0);
		ipv6h_out->payload_len  = htons(skb_e->len - iphdrlen);
		ipv6h_out->nexthdr      = IPPROTO_UDP;
		ipv6h_out->hop_limit    = ttl;
		ipv6h_out->daddr        = dst_vtepaddr->in6;
		ipv6h_out->saddr        = src_vtepaddr->in6;
	}

	if (output != NULL) {
		/*
		 * send interface dev replace with rt's dev,
		 * otherwise out dev wrong
		 */
		struct net_device *dev = skb_dst(skb_e)->dev;

		skb_e->dev = dev;
		skb_e->protocol = htons(outer_proto);
		return output(NULL, skb_e);
	}

	net = dev_net(dst->dev);
	ipvs = net_ipvs(net);
	if (sysctl_fast_xmit(ipvs) && peer_vtep != NULL)
		return ip_vs_fnat_upstream_fast_xmit_v6(skb_e, encap_info);

	/* Another hack: avoid icmp_send in ip_fragment */
	skb_e->ignore_df = 1;
	if (local_vtep.af == AF_INET) {
		IP_VS_XMIT(PF_INET, skb_e, (struct rtable *)dst, net);
	} else {
		IP_VS_XMIT(PF_INET6, skb_e, (struct rt6_info *)dst, net);
	}

	return 0;
}

/* Response transmit icmp to client
 * Used for NAT/LOCAL.
 */
int
ip_vs_normal_response_icmp_xmit(struct sk_buff *skb, struct ip_vs_protocol *pp,
				struct ip_vs_conn *cp, struct ip_vs_iphdr *iphdr)
{
	struct rtable *rt;	/* Route to the other host */
	int mtu;
	struct iphdr *iph = NULL;
	__u32 in_device_id = 0;
	int offset = iphdr->len;

	if (skb_ensure_writable(skb, offset))
		goto out;

	iph = ip_hdr(skb);
	if (local_vtep.network_type == IP_VS_NETWORK_VLAN && cp->indev)
		in_device_id = (__u32)cp->indev->ifindex;

	/* lookup route table */
	rt = __ip_vs_get_rt(cp->ipvs->net, cp->caddr.ip,
					IP_VS_RT_MODE_LOCAL |
					IP_VS_RT_MODE_NON_LOCAL |
					IP_VS_RT_MODE_RDR, NULL, in_device_id);
	if (rt == NULL)
		goto out;

	/* MTU checking */
	mtu = dst_mtu(&rt->dst);
	if ((skb->len > mtu) && (iph->frag_off & htons(IP_DF))) {
		IP_VS_DBG_RL_PKT(0, cp->af, pp, skb, 0,
				 "nat_response_icmp(): frag needed for");
		goto error_put;
	}

	if (skb_cow(skb, rt->dst.dev->hard_header_len))
		goto error_put;

	/* drop old route */
	skb_dst_drop(skb);
	skb_dst_set(skb, &rt->dst);

	if (ip_vs_nat_icmp(skb, pp, cp, 1))
		goto out;

	/* Another hack: avoid icmp_send in ip_fragment */
	skb->ignore_df = 1;

	IP_VS_XMIT(PF_INET, skb, rt, cp->ipvs->net);

	return NF_STOLEN;

error_put:
	ip_rt_put(rt);
out:
	return NF_DROP;
}

#ifdef CONFIG_IP_VS_IPV6

int
ip_vs_normal_response_icmp_xmit_v6(struct sk_buff *skb,
				   struct ip_vs_protocol *pp,
				   struct ip_vs_conn *cp, struct ip_vs_iphdr *iph)
{
	struct dst_entry *dst = NULL;
	struct rt6_info *rt = NULL;	/* Route to the other host */
	int mtu;
	struct frag_hdr *fhead;
	__u32 in_device_id = 0;
	int offset = iph->len;

	if (skb_ensure_writable(skb, offset))
		goto out;

	/* lookup route table */
	if (local_vtep.network_type == IP_VS_NETWORK_VLAN && cp->indev)
		in_device_id = (__u32)cp->indev->ifindex;


	dst = __ip_vs_get_rt_v6(cp->ipvs->net, &cp->caddr.in6, NULL, 0,
					IP_VS_RT_MODE_LOCAL |
					IP_VS_RT_MODE_NON_LOCAL |
					IP_VS_RT_MODE_RDR,
					in_device_id);
	if (dst == NULL)
		goto out;

	rt = (struct rt6_info *) dst;
	if (unlikely(ipv6_hdr(skb)->nexthdr == IPPROTO_FRAGMENT)) {
		mtu = dst_mtu(&rt->dst);
		fhead = (struct frag_hdr *)skb_transport_header(skb);
		if (skb->len > mtu && ((fhead->frag_off) & htons(IP_DF))) {
			dst_release(&rt->dst);
			IP_VS_DBG_RL("%s(): frag needed\n", __func__);
			goto out;
		}
	}

	if (skb_cow(skb, rt->dst.dev->hard_header_len))
		goto error_put;

	/* drop old route */
	skb_dst_drop(skb);
	skb_dst_set(skb, &rt->dst);

	if (ip_vs_nat_icmp_v6(skb, pp, cp, 1))
		goto out;

	/* Another hack: avoid icmp_send in ip_fragment */
	skb->ignore_df = 1;

	IP_VS_XMIT(PF_INET6, skb, rt, cp->ipvs->net);

	return NF_STOLEN;

error_put:
	dst_release(&rt->dst);
out:
	return NF_DROP;
}

#endif

/* Response transmit icmp to client
 * Used for NAT / local client / FULLNAT.
 */
int
ip_vs_fnat_response_icmp_xmit(struct sk_buff *skb, struct ip_vs_protocol *pp,
			      struct ip_vs_conn *cp, struct ip_vs_iphdr *iph)
{
	struct rtable *rt;	/* Route to the other host */
	int mtu;
	__u32 in_device_id = 0;

	if (local_vtep.network_type == IP_VS_NETWORK_VLAN && cp->indev)
		in_device_id = (__u32)cp->indev->ifindex;

	rt = __ip_vs_get_rt(cp->ipvs->net, cp->caddr.ip,
				IP_VS_RT_MODE_LOCAL |
				IP_VS_RT_MODE_NON_LOCAL |
				IP_VS_RT_MODE_RDR, NULL, in_device_id);
	if (rt == NULL)
		goto tx_error_icmp;

	/* MTU checking */
	mtu = dst_mtu(&rt->dst);
	if ((skb->len > mtu) && (iph->fragoffs & htons(IP_DF))) {
		ip_rt_put(rt);
		IP_VS_DBG_RL_PKT(0, cp->af, pp, skb, 0,
				 "fnat_response_icmp(): frag needed for");
		goto tx_error;
	}

	/* copy-on-write the packet before mangling it */
	if (skb_ensure_writable(skb, iph->len))
		goto tx_error_put;

	if (skb_cow(skb, rt->dst.dev->hard_header_len))
		goto tx_error_put;

	/* drop old route */
	skb_dst_drop(skb);
	skb_dst_set(skb, &rt->dst);

	if (ip_vs_nat_icmp(skb, pp, cp, 1))
		goto tx_error;

	/* Another hack: avoid icmp_send in ip_fragment */
	skb->ignore_df = 1;

	IP_VS_XMIT(PF_INET, skb, rt, cp->ipvs->net);

	return NF_STOLEN;

tx_error_icmp:
	dst_link_failure(skb);
tx_error:
	kfree_skb(skb);
	return NF_STOLEN;
tx_error_put:
	ip_rt_put(rt);
	goto tx_error;
}

#ifdef CONFIG_IP_VS_IPV6
int ip_vs_response_xmit_v6_over_vxlan(struct sk_buff *skb,
				      struct ip_vs_conn *cp,
				      struct ip_vs_protocol *pp,
				      struct ip_vs_iphdr *iph,
				      int fullnat)
{
	struct rtable *rt4 = NULL;      /* Route to the other host */
	struct dst_entry *dst = NULL;
	struct frag_hdr *frag_header = NULL;
	int err, mtu, iphdr_len, is_icmp = 0;
	unsigned int min_headroom;
	struct ipvs_vxlan_encapsulation_info encap_info;

	if (local_vtep.af == AF_INET6) {
		dst = __ip_vs_get_rt_v6(cp->ipvs->net, &cp->peer_vtep.vtepaddr.in6,
				NULL, 0,
				IP_VS_RT_MODE_LOCAL |
				IP_VS_RT_MODE_NON_LOCAL |
				IP_VS_RT_MODE_RDR,
				0);
		if (dst == NULL)
			goto error_icmp;
		iphdr_len = sizeof(struct ipv6hdr);
	} else {
		rt4 = __ip_vs_get_rt(cp->ipvs->net, cp->peer_vtep.vtepaddr.ip,
					IP_VS_RT_MODE_LOCAL |
					IP_VS_RT_MODE_NON_LOCAL |
					IP_VS_RT_MODE_RDR, NULL, 0);
		if (rt4 == NULL) {
			goto error_icmp;
		}
		dst = &rt4->dst;
		iphdr_len = sizeof(struct iphdr);
	}

	min_headroom = LL_RESERVED_SPACE(dst->dev) +
		       dst->header_len + VXLAN_HLEN +
		       iphdr_len + (skb_vlan_tag_present(skb) ?
		       VLAN_HLEN : 0);

	/* Need space for new headers (invalidates ip_h ptr) */
	err = skb_cow_head(skb, min_headroom);
	if (unlikely(err))
		goto error_put;

	skb_dst_drop(skb);
	skb_dst_set(skb, dst);

	if (ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6)
		is_icmp = 1;
	else if (ipv6_hdr(skb)->nexthdr == IPPROTO_FRAGMENT &&
		 pskb_may_pull(skb, sizeof(struct ipv6hdr) + sizeof(struct frag_hdr))) {
		frag_header = (struct frag_hdr *)(ipv6_hdr(skb) + 1);
		is_icmp = (frag_header->nexthdr == IPPROTO_ICMPV6);
	}

	if (is_icmp) {
		if (ip_vs_nat_icmp_v6(skb, pp, cp, 1))
			goto error_put;
	} else {
		if (fullnat) {
			if (pp->fnat_out_handler &&
			    !pp->fnat_out_handler(skb, pp, cp, iph))
				goto error;
			ipv6_hdr(skb)->saddr = cp->vaddr.in6;
			ipv6_hdr(skb)->daddr = cp->caddr.in6;
		} else {
			/* mangle the packet */
			if (pp->snat_handler && !pp->snat_handler(skb, pp, cp, iph))
				goto error;
			ipv6_hdr(skb)->saddr = cp->vaddr.in6;
		}
	}

	encap_info.vni = NULL;
	encap_info.peer_vtep = &cp->peer_vtep;
	encap_info.indev = cp->indev;
	memcpy(encap_info.inner_src_mac, cp->src_hwaddr, MAX_ADDR_LEN);
	memcpy(encap_info.inner_dst_mac, cp->dst_hwaddr, MAX_ADDR_LEN);
	encap_info.dport = cp->dport;

	mtu = (sysctl_dev_mtu(cp->ipvs) > (dst_mtu(dst) - IP_VS_VXLAN_ENCAP_MAX_LEN)) ?
	       (dst_mtu(dst) - IP_VS_VXLAN_ENCAP_MAX_LEN) : sysctl_dev_mtu(cp->ipvs);
	if (skb->len > mtu) {
		skb->ipvs_property = 1;
		skb_forward_csum(skb);
		/* Yet another hack: avoid icmp_send in ip_fragment */
		skb->ignore_df = 1;
		if (local_vtep.af == AF_INET6)
			ip_vs_ipv6_frag_over_vxlan(cp->ipvs, NULL, skb,
						      ip_vs_ip6_finish_output2,
						      &encap_info);
		else
			ip_vs_ipv6_frag_over_vxlan(cp->ipvs, NULL, skb,
						      ip_vs_ip_finish_output2,
						      &encap_info);
		return NF_STOLEN;
	}
	vxlan_encapsulation(skb, &encap_info, NULL);
	return NF_STOLEN;

error_icmp:
	dst_link_failure(skb);
error:
	kfree_skb(skb);
	return NF_STOLEN;
error_put:
	dst_release(dst);
	goto error;
}

int ip_vs_fnat_response_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
				     struct ip_vs_conn *cp, struct ip_vs_iphdr *iph)
{
	struct dst_entry *dst = NULL;
	struct rt6_info *rt = NULL;	/* Route to the other host */
	int mtu;
	struct frag_hdr *fhead;
	__u32 in_device_id = 0;

	/* lookup route table */
	if (local_vtep.network_type == IP_VS_NETWORK_VLAN && cp->indev)
		in_device_id = (__u32)cp->indev->ifindex;

	dst = __ip_vs_get_rt_v6(cp->ipvs->net, &cp->caddr.in6, NULL, 0,
					IP_VS_RT_MODE_LOCAL |
					IP_VS_RT_MODE_NON_LOCAL |
					IP_VS_RT_MODE_RDR,
					in_device_id);
	if (dst == NULL)
		goto tx_error_icmp;

	rt = (struct rt6_info *) dst;
	if (unlikely(ipv6_hdr(skb)->nexthdr == IPPROTO_FRAGMENT)) {
		mtu = dst_mtu(&rt->dst);
		fhead = (struct frag_hdr *)skb_transport_header(skb);
		if (skb->len > mtu && ((fhead->frag_off) & htons(IP_DF))) {
			dst_release(&rt->dst);
			IP_VS_DBG_RL("%s(): frag needed\n", __func__);
			goto tx_error;
		}
	}

	if (cp->peer_vtep.vni) {
		return ip_vs_response_xmit_v6_over_vxlan(skb, cp, pp, iph, 0);
	}

	/* copy-on-write the packet before mangling it */
	if (skb_ensure_writable(skb, iph->len))
		goto tx_error_put;

	if (skb_cow(skb, rt->dst.dev->hard_header_len))
		goto tx_error_put;

	/* drop old route */
	skb_dst_drop(skb);
	skb_dst_set(skb, &rt->dst);

	if (ip_vs_nat_icmp_v6(skb, pp, cp, 1))
		goto tx_error;

	/* Another hack: avoid icmp_send in ip_fragment */
	skb->ignore_df = 1;

	IP_VS_XMIT(PF_INET6, skb, rt, cp->ipvs->net);

	return NF_STOLEN;

tx_error_icmp:
	dst_link_failure(skb);
tx_error:
	kfree_skb(skb);
	return NF_STOLEN;
tx_error_put:
	dst_release(&rt->dst);
	goto tx_error;
}

#endif

/* just for nat/fullnat mode */
int
ip_vs_fast_response_xmit(struct sk_buff **pskb, struct ip_vs_protocol *pp,
			 struct ip_vs_conn *cp, struct ip_vs_iphdr *iph)
{
	struct sk_buff *skb = *pskb;
	struct ethhdr *eth;
	struct ip_vs_dest *dest = cp->dest;

	/*
	 * (fullnat and fnat_in_fnat mode) & IP_VS_CONN_F_FULLNAT
	 * == IP_VS_CONN_F_FULLNAT
	 */
	__u32 fullnat = (IP_VS_FWD_FULLNAT_METHOD(cp) == IP_VS_CONN_F_FULLNAT);

	if (!cp->indev)
		goto err;
	if (!gso_ok(skb, cp->indev) && (skb->len > cp->indev->mtu))
		goto err;

	/* Try to reuse skb */
	if (unlikely(skb_shared(skb) || skb_cloned(skb))) {
		struct sk_buff *new_skb = skb_copy(skb, GFP_ATOMIC);
		if (unlikely(new_skb == NULL))
			goto err;

		/* Drop old skb */
		kfree_skb(skb);
		skb = new_skb;
		IP_VS_INC_ESTATS(ip_vs_esmib, FAST_XMIT_SKB_COPY);
	}

	skb->dev = cp->indev;

	/* change ip, port. */
	if (fullnat) {
		/* mangle the packet */
		if (pp->fnat_out_handler && !pp->fnat_out_handler(skb, pp, cp, iph))
			goto err;

		ip_hdr(skb)->saddr = cp->vaddr.ip;
		ip_hdr(skb)->daddr = cp->caddr.ip;
	} else {
		/* mangle the packet */
		if (pp->snat_handler && !pp->snat_handler(skb, pp, cp, iph))
			goto err;

		ip_hdr(skb)->saddr = cp->vaddr.ip;
	}

	ip_send_check(ip_hdr(skb));

	if (unlikely(skb_headroom(skb) < LL_RESERVED_SPACE(skb->dev))) {
		struct sk_buff *skb2;

		IP_VS_ERR_RL("need more headroom! realloc skb\n");
		skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(skb->dev));
		if (skb2 == NULL)
			goto err;
		kfree_skb(skb);
		skb = skb2;
		IP_VS_INC_ESTATS(ip_vs_esmib, FAST_XMIT_SKB_COPY);
	}

	if (likely(skb_mac_header_was_set(skb))) {
		eth = eth_hdr(skb);
		memcpy(eth->h_dest, cp->src_hwaddr, ETH_ALEN);
		memcpy(eth->h_source, cp->dst_hwaddr, ETH_ALEN);
		skb->data = (unsigned char *)eth_hdr(skb);
		skb->len += sizeof(struct ethhdr);
	} else {
		eth = (struct ethhdr *)skb_push(skb, sizeof(struct ethhdr));
		if (eth == NULL) {
			IP_VS_ERR_RL("%s:eth is NULL", __func__);
			goto err;
		}

		skb_reset_mac_header(skb);
		memcpy(eth->h_dest, cp->src_hwaddr, ETH_ALEN);
		memcpy(eth->h_source, cp->dst_hwaddr, ETH_ALEN);
	}
	skb->protocol = eth->h_proto = htons(ETH_P_IP);
	skb->pkt_type = PACKET_OUTGOING;

	IP_VS_DBG_RL("%s: send skb to client!\n", __func__);

	/* Send the packet out */
	do {
		int ret = dev_queue_xmit(skb);
		if (ret != 0)
			IP_VS_ERR_RL("dev_queue_xmit failed! code:%d\n", ret);
	} while (0);

	if (likely(dest != NULL)) {
		IP_VS_INC_DEST_ESTATS(dest, in2out_xmit_suc);
	}

	IP_VS_INC_ESTATS(ip_vs_esmib, FAST_XMIT_PASS);
	return 0;
err:
	if (skb != *pskb)
		*pskb = skb;
	IP_VS_INC_ESTATS(ip_vs_esmib, FAST_XMIT_REJECT);
	return 1;
}

#ifdef CONFIG_IP_VS_IPV6
int ip_vs_fast_response_xmit_v6(struct sk_buff **pskb, struct ip_vs_protocol *pp,
				struct ip_vs_conn *cp, struct ip_vs_iphdr *iph)
{
	struct sk_buff *skb = *pskb;
	struct ethhdr *eth;

	/*
	 * (fullnat and fnat_in_fnat mode) & IP_VS_CONN_F_FULLNAT
	 * == IP_VS_CONN_F_FULLNAT
	 */
	__u32 fullnat = (IP_VS_FWD_FULLNAT_METHOD(cp) == IP_VS_CONN_F_FULLNAT);

	if (!cp->indev)
		goto err;
	if (!gso_ok(skb, cp->indev) && (skb->len > cp->indev->mtu))
		goto err;

	/* Try to reuse skb if possible */
	if (unlikely(skb_shared(skb) || skb_cloned(skb))) {
		struct sk_buff *new_skb = skb_copy(skb, GFP_ATOMIC);
		if (unlikely(new_skb == NULL))
			goto err;

		/* Drop old skb */
		kfree_skb(skb);
		skb = new_skb;
		IP_VS_INC_ESTATS(ip_vs_esmib, FAST_XMIT_SKB_COPY);
	}

	if (cp->peer_vtep.vni) {
		ip_vs_response_xmit_v6_over_vxlan(skb, cp, pp, iph, fullnat);
		return 0;
	}

	skb->dev = cp->indev;

	/* change ip, port. */
	if (fullnat) {
		/* mangle the packet */
		if (pp->fnat_out_handler && !pp->fnat_out_handler(skb, pp, cp, iph))
			goto err;

		ipv6_hdr(skb)->saddr = cp->vaddr.in6;
		ipv6_hdr(skb)->daddr = cp->caddr.in6;
	} else {
		/* mangle the packet */
		if (pp->snat_handler && !pp->snat_handler(skb, pp, cp, iph))
			goto err;

		ipv6_hdr(skb)->saddr = cp->vaddr.in6;
	}

	if (unlikely(skb_headroom(skb) < LL_RESERVED_SPACE(skb->dev))) {
		struct sk_buff *skb2;

		IP_VS_ERR_RL("need more headroom! realloc skb\n");
		skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(skb->dev));
		if (skb2 == NULL)
			goto err;
		kfree_skb(skb);
		skb = skb2;
	}

	if (likely(skb_mac_header_was_set(skb))) {
		eth = eth_hdr(skb);
		memcpy(eth->h_dest, cp->src_hwaddr, ETH_ALEN);
		memcpy(eth->h_source, cp->dst_hwaddr, ETH_ALEN);
		skb->data = (unsigned char *)eth_hdr(skb);
		skb->len += sizeof(struct ethhdr);
	} else {
		eth = (struct ethhdr *)skb_push(skb, sizeof(struct ethhdr));
		skb_reset_mac_header(skb);
		memcpy(eth->h_dest, cp->src_hwaddr, ETH_ALEN);
		memcpy(eth->h_source, cp->dst_hwaddr, ETH_ALEN);
	}
	skb->protocol = eth->h_proto = htons(ETH_P_IPV6);
	skb->pkt_type = PACKET_OUTGOING;

	IP_VS_DBG_RL("%s: send skb to client!\n", __func__);
	/* Send the packet out */
	do {
		int ret = dev_queue_xmit(skb);
		if (ret != 0)
			IP_VS_ERR_RL("dev_queue_xmit failed! code:%d\n", ret);
	} while (0);

	IP_VS_INC_ESTATS(ip_vs_esmib, FAST_XMIT_PASS);
	return 0;
err:
	if (skb != *pskb)
		*pskb = skb;
	IP_VS_INC_ESTATS(ip_vs_esmib, FAST_XMIT_REJECT);
	return 1;
}
#endif

/* Response transmit to client
 * Used for NAT/Local.
 */
int
ip_vs_normal_response_xmit(struct sk_buff *skb, struct ip_vs_protocol *pp,
			   struct ip_vs_conn *cp, struct ip_vs_iphdr *iph)
{
	struct rtable *rt;
	int mtu;
	__u32 in_device_id = 0;

	/* copy-on-write the packet before mangling it */
	if (skb_ensure_writable(skb, iph->len))
		goto drop;

	/* mangle the packet */
	if (pp->snat_handler && !pp->snat_handler(skb, pp, cp, iph))
		goto drop;

	ip_hdr(skb)->saddr = cp->vaddr.ip;
	ip_send_check(ip_hdr(skb));

	if (local_vtep.network_type == IP_VS_NETWORK_VLAN && cp->indev)
		in_device_id = (__u32)cp->indev->ifindex;

	/* lookup route table */
	rt = __ip_vs_get_rt(cp->ipvs->net, cp->caddr.ip,
				IP_VS_RT_MODE_LOCAL |
				IP_VS_RT_MODE_NON_LOCAL |
				IP_VS_RT_MODE_RDR, NULL, in_device_id);

	if (rt == NULL)
		goto drop;

	/* MTU checking */
	mtu = dst_mtu(&rt->dst);
	if ((skb->len > mtu) && (ip_hdr(skb)->frag_off & htons(IP_DF))) {
		ip_rt_put(rt);
		IP_VS_INC_ESTATS(ip_vs_esmib, RESPONSE_UNEXPECTED_MTU);
		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
		IP_VS_DBG_RL_PKT(0, cp->af, pp, skb, 0,
				 "handle_nat_response(): frag needed for");
		goto drop;
	}

	if (skb_cow(skb, rt->dst.dev->hard_header_len)) {
		ip_rt_put(rt);
		goto drop;
	}

	/* drop old route */
	skb_dst_drop(skb);
	skb_dst_set(skb, &rt->dst);

	/* Another hack: avoid icmp_send in ip_fragment */
	skb->ignore_df = 1;

	IP_VS_XMIT(PF_INET, skb, rt, cp->ipvs->net);

	return NF_STOLEN;

drop:
	kfree_skb(skb);
	return NF_STOLEN;
}

#ifdef CONFIG_IP_VS_IPV6
int ip_vs_normal_response_xmit_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
				  struct ip_vs_conn *cp, struct ip_vs_iphdr *iph)
{
	struct dst_entry *dst = NULL;
	struct rt6_info *rt = NULL;
	int mtu;
	struct frag_hdr *fhead;
	__u32 in_device_id = 0;

	/* copy-on-write the packet before mangling it */
	if (skb_ensure_writable(skb, iph->len))
		goto drop;

	/* mangle the packet */
	if (pp->snat_handler && !pp->snat_handler(skb, pp, cp, iph))
		goto drop;

	ipv6_hdr(skb)->saddr = cp->vaddr.in6;

	if (local_vtep.network_type == IP_VS_NETWORK_VLAN && cp->indev)
		in_device_id = (__u32)cp->indev->ifindex;

	/* lookup route table */
	dst = __ip_vs_get_rt_v6(cp->ipvs->net, &cp->caddr.in6, NULL, 0,
					IP_VS_RT_MODE_LOCAL |
					IP_VS_RT_MODE_NON_LOCAL |
					IP_VS_RT_MODE_RDR,
					in_device_id);
	if (dst == NULL)
		goto drop;

	rt = (struct rt6_info *) dst;
	/* MTU checking */
	if (unlikely(ipv6_hdr(skb)->nexthdr == IPPROTO_FRAGMENT)) {
		mtu = dst_mtu(&rt->dst);
		fhead = (struct frag_hdr *)skb_transport_header(skb);
		if (skb->len > mtu && ((fhead->frag_off) & htons(IP_DF))) {
			dst_release(&rt->dst);
			IP_VS_INC_ESTATS(ip_vs_esmib, RESPONSE_UNEXPECTED_MTU);
			icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
			IP_VS_DBG_RL_PKT(0, cp->af, pp, skb, 0,
					 "handle_fnat_response_v6(): frag needed for");
			goto drop;
		}
	}

	if (skb_cow(skb, rt->dst.dev->hard_header_len)) {
		dst_release(&rt->dst);
		goto drop;
	}

	/* drop old route */
	skb_dst_drop(skb);
	skb_dst_set(skb, &rt->dst);

	/* Another hack: avoid icmp_send in ip_fragment */
	skb->ignore_df = 1;


	IP_VS_XMIT(PF_INET6, skb, rt, cp->ipvs->net);

	return NF_STOLEN;

drop:
	kfree_skb(skb);
	return NF_STOLEN;
}

#endif

/* Response transmit to client
 * Used for FULLNAT.
 */
int
ip_vs_fnat_response_xmit(struct sk_buff *skb, struct ip_vs_protocol *pp,
			 struct ip_vs_conn *cp, struct ip_vs_iphdr *iph)
{
	struct rtable *rt;	/* Route to the other host */
	int mtu;
	struct ip_vs_dest *dest = cp->dest;
	__u32 in_device_id = 0;

	/*
	 * (fullnat and fnat_in_fnat mode )& IP_VS_CONN_F_FULLNAT
	 * == IP_VS_CONN_F_FULLNAT
	 */
	__u32 fullnat = (IP_VS_FWD_FULLNAT_METHOD(cp) == IP_VS_CONN_F_FULLNAT);

	if (sysctl_fast_xmit(cp->ipvs) && !ip_vs_fast_response_xmit(&skb, pp, cp, iph))
		return NF_STOLEN;

	/* lookup route table */
	if (local_vtep.network_type == IP_VS_NETWORK_VLAN && cp->indev)
		in_device_id = (__u32)cp->indev->ifindex;

	rt = __ip_vs_get_rt(cp->ipvs->net, cp->caddr.ip,
				IP_VS_RT_MODE_LOCAL |
				IP_VS_RT_MODE_NON_LOCAL |
				IP_VS_RT_MODE_RDR, NULL, in_device_id);

	if (rt == NULL)
		goto tx_error_icmp;

	/* MTU checking */
	mtu = dst_mtu(&rt->dst);
	if (!gso_ok(skb, rt->dst.dev) && (skb->len > mtu) &&
					(iph->fragoffs & htons(IP_DF))) {
		ip_rt_put(rt);
		IP_VS_INC_ESTATS(ip_vs_esmib, RESPONSE_UNEXPECTED_MTU);
		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
		IP_VS_DBG_RL_PKT(0, cp->af, pp, skb, 0,
				 "handle_fnat_response(): frag needed for");
		if (likely(dest != NULL)) {
			IP_VS_INC_DEST_ESTATS(dest, in2out_xmit_mtu);
		}
		goto tx_error;
	}

	/* copy-on-write the packet before mangling it */
	if (skb_ensure_writable(skb, iph->len))
		goto tx_error_put;

	if (skb_cow(skb, rt->dst.dev->hard_header_len))
		goto tx_error_put;

	/* drop old route */
	skb_dst_drop(skb);
	skb_dst_set(skb, &rt->dst);
	skb->dev = skb_dst(skb)->dev;

	if (fullnat) {
		/* mangle the packet */
		if (pp->fnat_out_handler && !pp->fnat_out_handler(skb, pp, cp, iph))
			goto tx_error;

		ip_hdr(skb)->saddr = cp->vaddr.ip;
		ip_hdr(skb)->daddr = cp->caddr.ip;
	} else {
		/* mangle the packet */
		if (pp->snat_handler && !pp->snat_handler(skb, pp, cp, iph))
			goto tx_error;

		ip_hdr(skb)->saddr = cp->vaddr.ip;
	}
	ip_send_check(ip_hdr(skb));

	/* Another hack: avoid icmp_send in ip_fragment */
	skb->ignore_df = 1;
	if (likely(dest != NULL)) {
		IP_VS_INC_DEST_ESTATS(dest, in2out_xmit_suc);
	}

	IP_VS_XMIT(PF_INET, skb, rt, cp->ipvs->net);

	return NF_STOLEN;

tx_error_icmp:
	dst_link_failure(skb);
tx_error:
	kfree_skb(skb);
	return NF_STOLEN;
tx_error_put:
	ip_rt_put(rt);
	goto tx_error;
}

#ifdef CONFIG_IP_VS_IPV6

int
ip_vs_fnat_response_xmit_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
			    struct ip_vs_conn *cp, struct ip_vs_iphdr *iph)
{
	struct dst_entry *dst = NULL;
	struct rt6_info *rt = NULL;	/* Route to the other host */
	int mtu;
	struct frag_hdr *fhead;
	__u32 in_device_id = 0;

	if (sysctl_fast_xmit(cp->ipvs) && !ip_vs_fast_response_xmit_v6(&skb, pp, cp, iph))
		return NF_STOLEN;

	/* lookup route table */
	if (local_vtep.network_type == IP_VS_NETWORK_VLAN && cp->indev)
		in_device_id = (__u32)cp->indev->ifindex;

	dst = __ip_vs_get_rt_v6(cp->ipvs->net, &cp->caddr.in6, NULL, 0,
					IP_VS_RT_MODE_LOCAL |
					IP_VS_RT_MODE_NON_LOCAL |
					IP_VS_RT_MODE_RDR,
					in_device_id);
	if (dst == NULL)
		goto tx_error_icmp;

	rt = (struct rt6_info *) dst;
	if (unlikely(ipv6_hdr(skb)->nexthdr == IPPROTO_FRAGMENT)) {
		mtu = dst_mtu(&rt->dst);
		fhead = (struct frag_hdr *)skb_transport_header(skb);
		if (!gso_ok(skb, rt->dst.dev) && (skb->len > mtu) &&
			    ((fhead->frag_off) & htons(IP_DF))) {
			dst_release(&rt->dst);
			IP_VS_INC_ESTATS(ip_vs_esmib, RESPONSE_UNEXPECTED_MTU);
			icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
			IP_VS_DBG_RL_PKT(0, cp->af, pp, skb, 0,
					 "handle_fnat_response_v6(): frag needed for");
			goto tx_error;
		}
	}

	if (cp->peer_vtep.vni) {
		return ip_vs_response_xmit_v6_over_vxlan(skb, cp, pp, iph, 1);
	}

	/* copy-on-write the packet before mangling it */
	if (skb_ensure_writable(skb, iph->len))
		goto tx_error_put;

	if (skb_cow(skb, rt->dst.dev->hard_header_len))
		goto tx_error_put;

	/* drop old route */
	skb_dst_drop(skb);
	skb_dst_set(skb, &rt->dst);
	skb->dev = skb_dst(skb)->dev;

	/* mangle the packet */
	if (pp->fnat_out_handler && !pp->fnat_out_handler(skb, pp, cp, iph))
		goto tx_error;

	ipv6_hdr(skb)->saddr = cp->vaddr.in6;
	ipv6_hdr(skb)->daddr = cp->caddr.in6;

	/* Another hack: avoid icmp_send in ip_fragment */
	skb->ignore_df = 1;

	IP_VS_XMIT(PF_INET6, skb, rt, cp->ipvs->net);

	return NF_STOLEN;

tx_error_icmp:
	dst_link_failure(skb);
tx_error:
	kfree_skb(skb);
	return NF_STOLEN;
tx_error_put:
	dst_release(&rt->dst);
	goto tx_error;
}
#endif



/* return NF_ACCEPT to allow forwarding or other NF_xxx on error */
static inline int ip_vs_tunnel_xmit_prepare(struct sk_buff *skb,
					    struct ip_vs_conn *cp)
{
	int ret = NF_ACCEPT;

	skb->ipvs_property = 1;
	if (unlikely(cp->flags & IP_VS_CONN_F_NFCT))
		ret = ip_vs_confirm_conntrack(skb);
	if (ret == NF_ACCEPT) {
		nf_reset_ct(skb);
		skb_forward_csum(skb);
	}
	return ret;
}

/* In the event of a remote destination, it's possible that we would have
 * matches against an old socket (particularly a TIME-WAIT socket). This
 * causes havoc down the line (ip_local_out et. al. expect regular sockets
 * and invalid memory accesses will happen) so simply drop the association
 * in this case.
*/
static inline void ip_vs_drop_early_demux_sk(struct sk_buff *skb)
{
	/* If dev is set, the packet came from the LOCAL_IN callback and
	 * not from a local TCP socket.
	 */
	if (skb->dev)
		skb_orphan(skb);
}

/* return NF_STOLEN (sent) or NF_ACCEPT if local=1 (not sent) */
static inline int ip_vs_nat_send_or_cont(int pf, struct sk_buff *skb,
					 struct ip_vs_conn *cp, int local)
{
	int ret = NF_STOLEN;

	skb->ipvs_property = 1;
	if (likely(!(cp->flags & IP_VS_CONN_F_NFCT)))
		ip_vs_notrack(skb);
	else
		ip_vs_update_conntrack(skb, cp, 1);

	/* Remove the early_demux association unless it's bound for the
	 * exact same port and address on this host after translation.
	 */
	if (!local || cp->vport != cp->dport ||
	    !ip_vs_addr_equal(cp->af, &cp->vaddr, &cp->daddr))
		ip_vs_drop_early_demux_sk(skb);

	if (!local) {
		skb_forward_csum(skb);
		NF_HOOK(pf, NF_INET_LOCAL_OUT, cp->ipvs->net, NULL, skb,
			NULL, skb_dst(skb)->dev, dst_output);
	} else
		ret = NF_ACCEPT;

	return ret;
}

/* return NF_STOLEN (sent) or NF_ACCEPT if local=1 (not sent) */
static inline int ip_vs_send_or_cont(int pf, struct sk_buff *skb,
				     struct ip_vs_conn *cp, int local)
{
	int ret = NF_STOLEN;

	skb->ipvs_property = 1;
	if (likely(!(cp->flags & IP_VS_CONN_F_NFCT)))
		ip_vs_notrack(skb);
	if (!local) {
		ip_vs_drop_early_demux_sk(skb);
		skb_forward_csum(skb);
		NF_HOOK(pf, NF_INET_LOCAL_OUT, cp->ipvs->net, NULL, skb,
			NULL, skb_dst(skb)->dev, dst_output);
	} else
		ret = NF_ACCEPT;
	return ret;
}

static inline int __ip_vs_hck_skb_csum(struct sk_buff *skb,
				       struct ip_vs_iphdr *iph)
{
	__wsum csum;
	__sum16 *pchk = NULL;
	uint32_t offset;
	u8 pchk_offset;
	void *c_hdr = (void *)(skb_network_header(skb) + iph->len);

	if (iph->protocol == IPPROTO_UDP) {
		struct udphdr *udph = c_hdr;
		pchk = &udph->check;
		offset = sizeof(struct udphdr);
	} else if (iph->protocol == IPPROTO_TCP) {
		struct tcphdr *tcph = c_hdr;
		pchk = &tcph->check;
		offset = sizeof(struct tcphdr);
	} else if (iph->protocol == IPPROTO_ICMP) {
		struct icmphdr *icmp_h = c_hdr;
		pchk = &icmp_h->checksum;
		offset = sizeof(struct icmphdr);
	} else if (iph->protocol == IPPROTO_ICMPV6) {
		struct icmp6hdr _icmph = {}, *icmp6h_ptr = NULL;
		icmp6h_ptr = skb_header_pointer(skb, iph->len, sizeof(_icmph), &_icmph);
		if (icmp6h_ptr == NULL)
			return -1;
		pchk = &icmp6h_ptr->icmp6_cksum;
		offset = sizeof(struct icmp6hdr);
	} else {
		return -1;
	}

	pchk_offset = (u8 *)pchk - skb_network_header(skb);
	if (skb_ensure_writable(skb, (iph->len + offset)))
		return -1;

	pchk = (__sum16 *)(skb_network_header(skb) + pchk_offset);
	*pchk = 0;
	csum = skb_checksum(skb, iph->len, skb->len - iph->len, 0);
	if (iph->protocol != IPPROTO_ICMP && iph->protocol != IPPROTO_ICMPV6) {
		if (skb->protocol == htons(ETH_P_IP))
			*pchk = csum_tcpudp_magic(iph->saddr.ip, iph->daddr.ip,
						  (skb->len - iph->len), iph->protocol, csum);
		else
			*pchk = csum_ipv6_magic(&iph->saddr.in6, &iph->daddr.in6,
						(skb->len - iph->len), iph->protocol, csum);
	} else
		*pchk = csum_fold(csum);

	return 0;
}


typedef int (*ip_vs_hck_finish_output2)(struct sock *sk, struct sk_buff *skb);
unsigned int ip_vs_fnat_hck_xmit(struct net *net, struct sk_buff *skb,
		struct ip_vs_iphdr *iph, struct ip_vs_hck_dest *dst)
{
	struct dst_entry *dst_e = NULL;
	ip_vs_hck_finish_output2 output2 = NULL;
	struct ipvs_vxlan_encapsulation_info encap_info;

	if (local_vtep.af == AF_INET) {
		struct rtable *rt = NULL;
		local_bh_disable();
		rt = __ip_vs_get_rt(net, dst->vni.vtepaddr.ip,
				IP_VS_RT_MODE_LOCAL |
				IP_VS_RT_MODE_NON_LOCAL |
				IP_VS_RT_MODE_RDR, &local_vtep.vtepaddr.ip, 0);
		local_bh_enable();
		if (rt == NULL) {
			/* Let kernel handle that case. */
			IP_VS_INC_ESTATS(ip_vs_esmib, HCK_DEST_OUT_RT_NOT_FOUND);
			return NF_ACCEPT;
		}
		dst_e = &rt->dst;
		output2 = ip_vs_ip_finish_output2;
	} else {
		local_bh_disable();

		dst_e = __ip_vs_get_rt_v6(net, &dst->vni.vtepaddr.in6,
				&local_vtep.vtepaddr.in6, 0,
				IP_VS_RT_MODE_LOCAL |
				IP_VS_RT_MODE_NON_LOCAL |
				IP_VS_RT_MODE_RDR,
				0);
		local_bh_enable();
		if (dst_e == NULL) {
			IP_VS_INC_ESTATS(ip_vs_esmib, HCK_DEST_OUT_RT_NOT_FOUND);
			return NF_ACCEPT;
		}
		output2 = ip_vs_ip6_finish_output2;
	}

	if (skb->ip_summed == CHECKSUM_PARTIAL &&
		__ip_vs_hck_skb_csum(skb, iph)) {
		IP_VS_INC_ESTATS(ip_vs_esmib, HCK_DEST_OUTPUT_DROP);
		dst_release(dst_e);
		return NF_DROP;
	}

	skb_dst_drop(skb);
	skb_dst_set(skb, dst_e);
	/* health check ximit runs in sigle thread. */
	dst->vni.sport = udp_flow_src_port(net, skb, 0, 0, false);
	encap_info.peer_vtep = NULL;
	encap_info.vni = &dst->vni;
	if (vxlan_encapsulation(skb, &encap_info, output2)) {
		IP_VS_INC_ESTATS(ip_vs_esmib, HCK_DEST_OUTPUT_DROP);
	}
	return NF_STOLEN;
}

extern ipvs_ip6_input_sym ip6_input_sym;
extern ipvs_ip6_input_sym ip_input_sym;

unsigned int ip_vs_hck_response(struct netns_ipvs *ipvs, int af,
				struct sk_buff *skb,
				struct ip_vs_iphdr *iph,
				struct ip_vs_hck_dest *dst)
{
	ipvs_ip6_input_sym input_handler = NULL;

	/* Nothing need to do if same flow. */
	if (af == local_vtep.af)
		return NF_ACCEPT;

	if (af == AF_INET) {
		struct iphdr *iph4 = ip_hdr(skb);
		int err;

		if (!skb_valid_dst(skb)) {
			err = ip_route_input_noref(skb, iph4->daddr, iph4->saddr,
						   iph4->tos, skb->dev);
			if (unlikely(err)) {
				IP_VS_INC_ESTATS(ip_vs_esmib, HCK_DEST_IN_RT_NOT_FOUND);
				return NF_ACCEPT;
			}
		}
		input_handler = ip_input_sym;
		memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));
	} else {
		struct dst_entry *dst = NULL;
		dst = __ip_vs_get_rt_v6(ipvs->net, &iph->daddr.in6, NULL, 0,
				IP_VS_RT_MODE_LOCAL |
				IP_VS_RT_MODE_NON_LOCAL |
				IP_VS_RT_MODE_RDR,
				0);
		if (dst == NULL) {
			IP_VS_INC_ESTATS(ip_vs_esmib, HCK_DEST_IN_RT_NOT_FOUND);
			return NF_ACCEPT;
		}
		IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr);
		input_handler = ip6_input_sym;
		skb_dst_drop(skb);
		skb_dst_set(skb, dst);
	}

	if (input_handler(skb))
		IP_VS_INC_ESTATS(ip_vs_esmib, HCK_DEST_INPUT_DROP);

	return NF_STOLEN;
}

/*
 *      NULL transmitter (do nothing except return NF_ACCEPT)
 */
int
ip_vs_null_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
		struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
{
	/* we do not touch skb and do not need pskb ptr */
	return ip_vs_send_or_cont(NFPROTO_IPV4, skb, cp, 1);
}


/*
 *      Bypass transmitter
 *      Let packets bypass the destination when the destination is not
 *      available, it may be only used in transparent cache cluster.
 */
int
ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
		  struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
{
	struct iphdr  *iph = ip_hdr(skb);

	EnterFunction(10);

	if (__ip_vs_get_out_rt(cp->ipvs, skb, cp, iph->daddr,
			       IP_VS_RT_MODE_NON_LOCAL, NULL, ipvsh) < 0)
		goto tx_error;

	ip_send_check(iph);

	/* Another hack: avoid icmp_send in ip_fragment */
	skb->ignore_df = 1;

	ip_vs_send_or_cont(NFPROTO_IPV4, skb, cp, 0);

	LeaveFunction(10);
	return NF_STOLEN;

 tx_error:
	kfree_skb(skb);
	LeaveFunction(10);
	return NF_STOLEN;
}

#ifdef CONFIG_IP_VS_IPV6
int
ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
		     struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
{
	struct ipv6hdr *iph = ipv6_hdr(skb);

	EnterFunction(10);

	if (__ip_vs_get_out_rt_v6(cp->ipvs, skb, cp,
				  &iph->daddr, NULL,
				  ipvsh, 0, IP_VS_RT_MODE_NON_LOCAL) < 0)
		goto tx_error;

	/* Another hack: avoid icmp_send in ip_fragment */
	skb->ignore_df = 1;

	ip_vs_send_or_cont(NFPROTO_IPV6, skb, cp, 0);

	LeaveFunction(10);
	return NF_STOLEN;

 tx_error:
	kfree_skb(skb);
	LeaveFunction(10);
	return NF_STOLEN;
}
#endif

void
ip_vs_save_xmit_info(struct sk_buff *skb, struct ip_vs_protocol *pp,
					struct ip_vs_conn *cp)
{
	uint32_t offset_len = 0;
	struct ipv6hdr *ipv6h_out = NULL;
	struct iphdr *iph_out = NULL;
	struct ethhdr *ethhdr_out = NULL;

	if (!sysctl_fast_xmit(cp->ipvs))
		return;

	if (!skb->dev) {
		IP_VS_INC_ESTATS(ip_vs_esmib, FAST_XMIT_DEV_LOST);
		IP_VS_DBG_RL("save_xmit_info, skb->dev is NULL. \n");
		return;
	}
	IP_VS_DBG(9, "save_xmit_info, netdevice:%s\n", netdev_name(skb->dev));

	if (likely((skb->dev->type == ARPHRD_ETHER) &&
					skb_mac_header_was_set(skb))) {
		struct ethhdr *eth = (struct ethhdr *)skb_mac_header(skb);

		if (unlikely(cp->indev == NULL)) {
			cp->indev = skb->dev;
			dev_hold(cp->indev);
		}

		if (unlikely(cp->indev != skb->dev)) {
			dev_put(cp->indev);
			cp->indev = skb->dev;
			dev_hold(cp->indev);
		}

		memcpy(cp->src_hwaddr, eth->h_source, ETH_ALEN);
		memcpy(cp->dst_hwaddr, eth->h_dest, ETH_ALEN);

		if (cp->peer_vtep.vni) {
			if (local_vtep.af == AF_INET6) {
				offset_len = VXLAN_IPV6HDR_OSFFSET;
				ipv6h_out = (struct ipv6hdr *)(skb_mac_header(skb) - offset_len);
				cp->peer_vtep.vtepaddr.in6 = ipv6h_out->saddr;
			} else {
				offset_len = VXLAN_IPHDR_OSFFSET;
				iph_out = (struct iphdr *)(skb_mac_header(skb) - offset_len);
				cp->peer_vtep.vtepaddr.ip = iph_out->saddr;
			}
			offset_len += sizeof(struct ethhdr);
			ethhdr_out = (struct ethhdr *)(skb_mac_header(skb) - offset_len);
			ether_addr_copy(cp->peer_vtep.out_src_mac, ethhdr_out->h_source);
			ether_addr_copy(cp->peer_vtep.out_dst_mac, ethhdr_out->h_dest);
		}
	} else {
		IP_VS_INC_ESTATS(ip_vs_esmib, FAST_XMIT_NO_MAC);
		IP_VS_DBG_RL("save dev and mac failed!\n");
	}
}

/*
 *      NAT transmitter (only for outside-to-inside nat forwarding)
 *      Not used for related ICMP
 */
int
ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
	       struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
{
	struct rtable *rt;		/* Route to the other host */
	int local, rc, was_input;

	EnterFunction(10);

	/* check if it is a connection of no-client-port */
	if (unlikely(cp->flags & IP_VS_CONN_F_NO_CPORT)) {
		__be16 _pt, *p;

		p = skb_header_pointer(skb, ipvsh->len, sizeof(_pt), &_pt);
		if (p == NULL)
			goto tx_error;
		ip_vs_conn_fill_cport(cp, *p);
		IP_VS_DBG(10, "filled cport=%d\n", ntohs(*p));
	}

	was_input = rt_is_input_route(skb_rtable(skb));
	local = __ip_vs_get_out_rt(cp->ipvs, skb, cp, cp->daddr.ip,
				   IP_VS_RT_MODE_LOCAL |
				   IP_VS_RT_MODE_NON_LOCAL |
				   IP_VS_RT_MODE_RDR, NULL, ipvsh);
	if (local < 0)
		goto tx_error;
	rt = skb_rtable(skb);
	/*
	 * Avoid duplicate tuple in reply direction for NAT traffic
	 * to local address when connection is sync-ed
	 */
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
	if (cp->flags & IP_VS_CONN_F_SYNC && local) {
		enum ip_conntrack_info ctinfo;
		struct nf_conn *ct = nf_ct_get(skb, &ctinfo);

		if (ct) {
			IP_VS_DBG_RL_PKT(10, AF_INET, pp, skb, ipvsh->off,
					 "ip_vs_nat_xmit(): "
					 "stopping DNAT to local address");
			goto tx_error;
		}
	}
#endif

	/* From world but DNAT to loopback address? */
	if (local && ipv4_is_loopback(cp->daddr.ip) && was_input) {
		IP_VS_DBG_RL_PKT(1, AF_INET, pp, skb, ipvsh->off,
				 "ip_vs_nat_xmit(): stopping DNAT to loopback "
				 "address");
		goto tx_error;
	}

	/* copy-on-write the packet before mangling it */
	if (skb_ensure_writable(skb, sizeof(struct iphdr)))
		goto tx_error;

	if (skb_cow(skb, rt->dst.dev->hard_header_len))
		goto tx_error;

	/* mangle the packet */
	if (pp->dnat_handler && !pp->dnat_handler(skb, pp, cp, ipvsh))
		goto tx_error;
	ip_hdr(skb)->daddr = cp->daddr.ip;
	ip_send_check(ip_hdr(skb));

	IP_VS_DBG_PKT(10, AF_INET, pp, skb, ipvsh->off, "After DNAT");

	/* FIXME: when application helper enlarges the packet and the length
	   is larger than the MTU of outgoing device, there will be still
	   MTU problem. */

	/* Another hack: avoid icmp_send in ip_fragment */
	skb->ignore_df = 1;

	rc = ip_vs_nat_send_or_cont(NFPROTO_IPV4, skb, cp, local);

	LeaveFunction(10);
	return rc;

  tx_error:
	kfree_skb(skb);
	LeaveFunction(10);
	return NF_STOLEN;
}

#ifdef CONFIG_IP_VS_IPV6
int
ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
		  struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
{
	struct rt6_info *rt;		/* Route to the other host */
	int local, rc;

	EnterFunction(10);

	/* check if it is a connection of no-client-port */
	if (unlikely(cp->flags & IP_VS_CONN_F_NO_CPORT && !ipvsh->fragoffs)) {
		__be16 _pt, *p;
		p = skb_header_pointer(skb, ipvsh->len, sizeof(_pt), &_pt);
		if (p == NULL)
			goto tx_error;
		ip_vs_conn_fill_cport(cp, *p);
		IP_VS_DBG(10, "filled cport=%d\n", ntohs(*p));
	}

	local = __ip_vs_get_out_rt_v6(cp->ipvs, skb, cp,
				      &cp->daddr.in6,
				      NULL, ipvsh, 0,
				      IP_VS_RT_MODE_LOCAL |
				      IP_VS_RT_MODE_NON_LOCAL |
				      IP_VS_RT_MODE_RDR);
	if (local < 0)
		goto tx_error;
	rt = (struct rt6_info *) skb_dst(skb);
	/*
	 * Avoid duplicate tuple in reply direction for NAT traffic
	 * to local address when connection is sync-ed
	 */
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
	if (cp->flags & IP_VS_CONN_F_SYNC && local) {
		enum ip_conntrack_info ctinfo;
		struct nf_conn *ct = nf_ct_get(skb, &ctinfo);

		if (ct) {
			IP_VS_DBG_RL_PKT(10, AF_INET6, pp, skb, ipvsh->off,
					 "ip_vs_nat_xmit_v6(): "
					 "stopping DNAT to local address");
			goto tx_error;
		}
	}
#endif

	/* From world but DNAT to loopback address? */
	if (local && skb->dev && !(skb->dev->flags & IFF_LOOPBACK) &&
	    ipv6_addr_type(&cp->daddr.in6) & IPV6_ADDR_LOOPBACK) {
		IP_VS_DBG_RL_PKT(1, AF_INET6, pp, skb, ipvsh->off,
				 "ip_vs_nat_xmit_v6(): "
				 "stopping DNAT to loopback address");
		goto tx_error;
	}

	/* copy-on-write the packet before mangling it */
	if (skb_ensure_writable(skb, sizeof(struct ipv6hdr)))
		goto tx_error;

	if (skb_cow(skb, rt->dst.dev->hard_header_len))
		goto tx_error;

	/* mangle the packet */
	if (pp->dnat_handler && !pp->dnat_handler(skb, pp, cp, ipvsh))
		goto tx_error;
	ipv6_hdr(skb)->daddr = cp->daddr.in6;

	IP_VS_DBG_PKT(10, AF_INET6, pp, skb, ipvsh->off, "After DNAT");

	/* FIXME: when application helper enlarges the packet and the length
	   is larger than the MTU of outgoing device, there will be still
	   MTU problem. */

	/* Another hack: avoid icmp_send in ip_fragment */
	skb->ignore_df = 1;

	rc = ip_vs_nat_send_or_cont(NFPROTO_IPV6, skb, cp, local);

	LeaveFunction(10);
	return rc;

tx_error:
	LeaveFunction(10);
	kfree_skb(skb);
	return NF_STOLEN;
}
#endif

/*
 *      FULLNAT transmitter (only for outside-to-inside fullnat forwarding)
 *      Not used for related ICMP
 */
int
ip_vs_fnat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
		struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
{
	struct rtable *rt;	/* Route to the other host */
	int mtu, err, min_headroom;
	int encaphdrlen = 0;
	struct iphdr *iph = ip_hdr(skb);
	struct ip_vs_dest *dest = cp->dest;
	struct ipvs_vxlan_encapsulation_info encap_info;
	__u32 vni = 0;
	__u32 srcaddr = 0;
	int local;

	EnterFunction(10);

	if (local_vtep.network_type != IP_VS_NETWORK_VLAN)
		vni = cp->vni.out_vni;

	/* check if it is a connection of no-client-port */
	if (unlikely(cp->flags & IP_VS_CONN_F_NO_CPORT)) {
		__be16 _pt, *p;
		p = skb_header_pointer(skb, iph->ihl * 4, sizeof(_pt), &_pt);
		if (p == NULL)
			goto tx_error;
		ip_vs_conn_fill_cport(cp, *p);
		IP_VS_DBG(10, "filled cport=%d\n", ntohs(*p));
	}

	if (vni != 0)
		srcaddr = local_vtep.vtepaddr.ip;
	local = __ip_vs_get_out_rt(cp->ipvs, skb, cp, cp->daddr.ip,
				IP_VS_RT_MODE_LOCAL |
				IP_VS_RT_MODE_NON_LOCAL |
				IP_VS_RT_MODE_RDR, &srcaddr, ipvsh);
	if (local < 0)
		goto tx_error;

	rt = skb_rtable(skb);
	if (rt == NULL)
		goto tx_error;

	/* MTU checking */
	mtu = dst_mtu(&rt->dst);
	if (vni) {
		encaphdrlen = sizeof(struct iphdr) + VXLAN_HLEN + sizeof(struct ethhdr);
	}
	if (!gso_ok(skb, rt->dst.dev) && ((skb->len + encaphdrlen) > mtu) &&
		    (iph->frag_off & htons(IP_DF))) {
		IP_VS_INC_ESTATS(ip_vs_esmib, XMIT_UNEXPECTED_MTU);
		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
		IP_VS_DBG_RL_PKT(0, cp->af, pp, skb, 0,
				 "ip_vs_fnat_xmit(): frag needed for");
		if (likely(dest != NULL)) {
			IP_VS_INC_DEST_ESTATS(dest, out2in_xmit_mtu);
		}
		goto tx_error;
	}
	ip_vs_save_xmit_info(skb, pp, cp);

	if (vni) {
		min_headroom = LL_RESERVED_SPACE(rt->dst.dev)
		+ rt->dst.header_len
		+ VXLAN_HLEN + sizeof(struct iphdr)
		+ (skb_vlan_tag_present(skb) ? VLAN_HLEN : 0);

		/* Need space for new headers (invalidates iph ptr) */
		err = skb_cow_head(skb, min_headroom);
		if (unlikely(err))
			goto tx_error;

		/* mangle the packet */
		if ((cp->flags & IP_VS_CONN_F_FWD_MASK)
			== IP_VS_CONN_F_NAT_IN_FULLNAT) {
			if (pp->dnat_handler && !pp->dnat_handler(skb, pp, cp, ipvsh))
				goto tx_error;
			ip_hdr(skb)->daddr = cp->daddr.ip;
		} else {
			if (pp->fnat_in_handler &&
			    !pp->fnat_in_handler(&skb, pp, cp, ipvsh))
				goto tx_error;
			ip_hdr(skb)->saddr = cp->laddr.ip;
			ip_hdr(skb)->daddr = cp->daddr.ip;
		}
		ip_send_check(ip_hdr(skb));
		IP_VS_DBG(9,
			"local mac:%02x:%02x:%02x:%02x:%02x:%02x, mod=%d\n",
			rt->dst.dev->dev_addr[0], rt->dst.dev->dev_addr[1],
			rt->dst.dev->dev_addr[2], rt->dst.dev->dev_addr[3],
			rt->dst.dev->dev_addr[4], rt->dst.dev->dev_addr[5],
				(cp->flags & IP_VS_CONN_F_FWD_MASK));

		mtu = (sysctl_dev_mtu(cp->ipvs) >
		       (dst_mtu(&rt->dst) - IP_VS_VXLAN_ENCAP_MAX_LEN)) ?
			(dst_mtu(&rt->dst) - IP_VS_VXLAN_ENCAP_MAX_LEN) :
			sysctl_dev_mtu(cp->ipvs);
		if (skb->len > mtu) {
			skb->ipvs_property = 1;
			skb_forward_csum(skb);
			/* Another hack: avoid icmp_send in ip_fragment */
			skb->ignore_df = 1;
			ip_vs_ip_fragment(cp->ipvs, NULL, skb,
				ip_vs_ip_finish_output2, cp);
			goto out;
		}
		encap_info.peer_vtep = NULL;
		encap_info.vni = &cp->vni;
		vxlan_encapsulation(skb, &encap_info, NULL);
		goto out;
	} else {
		/* copy-on-write the packet before mangling it */
		if (skb_ensure_writable(skb, sizeof(struct iphdr)))
			goto tx_error;

		if (skb_cow(skb, rt->dst.dev->hard_header_len))
			goto tx_error;

		/* mangle the packet */
		if (pp->fnat_in_handler && !pp->fnat_in_handler(&skb, pp, cp, ipvsh))
			goto tx_error;
		ip_hdr(skb)->saddr = cp->laddr.ip;
		ip_hdr(skb)->daddr = cp->daddr.ip;
		ip_send_check(ip_hdr(skb));
		IP_VS_DBG_PKT(10, cp->af, pp, skb, 0, "After FNAT-IN");
	}

	/* FIXME: when application helper enlarges the packet and the length
	   is larger than the MTU of outgoing device, there will be still
	   MTU problem. */

	/* Another hack: avoid icmp_send in ip_fragment */
	skb->ignore_df = 1;

	if (likely(dest != NULL)) {
		IP_VS_INC_DEST_ESTATS(dest, out2in_xmit_suc);
	}
	IP_VS_XMIT(PF_INET, skb, rt, cp->ipvs->net);

out:
	LeaveFunction(10);
	return NF_STOLEN;

tx_error:
	kfree_skb(skb);
	LeaveFunction(10);
	return NF_STOLEN;
}

#ifdef CONFIG_IP_VS_IPV6
int ip_vs_fnat_xmit_v6_over_vxlan(struct sk_buff *skb, struct ip_vs_conn *cp,
				  struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
{
	struct dst_entry *dst = NULL;
	int mtu, err, min_headroom, iphdrlen, encaphdrlen;
	struct frag_hdr *fhead;
	struct ipv6hdr *ipv6_h = ipv6_hdr(skb);
	struct ipvs_vxlan_encapsulation_info encap_info;
	int local;

	EnterFunction(10);

	local = __ip_vs_get_vtep_dst(skb, cp, &local_vtep.vtepaddr, ipvsh, 0,
				IP_VS_RT_MODE_LOCAL |
				IP_VS_RT_MODE_NON_LOCAL |
				IP_VS_RT_MODE_RDR);
	if (local < 0)
		goto tx_error;

	dst = skb_dst(skb);
	if (dst == NULL) {
		goto tx_error;
	}

	if (local_vtep.af == AF_INET) {
		iphdrlen = sizeof(struct iphdr);
	} else {
		iphdrlen = sizeof(struct ipv6hdr);
	}

	if (unlikely(ipv6_h->nexthdr == IPPROTO_FRAGMENT)) {
		mtu = dst_mtu(dst);
		encaphdrlen = iphdrlen + VXLAN_HLEN + sizeof(struct ethhdr);
		fhead = (struct frag_hdr *)skb_transport_header(skb);
		if (!gso_ok(skb, dst->dev) && ((skb->len + encaphdrlen) > mtu) &&
		    ((fhead->frag_off) & htons(IP_DF))) {
			IP_VS_INC_ESTATS(ip_vs_esmib, XMIT_VXLAN_UNEXPECTED_MTU);
			icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
			IP_VS_DBG_RL_PKT(10, cp->af, pp, skb, 0,
					"ip_vs_fnat_xmit_v6_over_vxlan: frag needed for");
			goto tx_error;
		}
	}

	ip_vs_save_xmit_info(skb, pp, cp);

	min_headroom = LL_RESERVED_SPACE(dst->dev) + dst->header_len +
		       VXLAN_HLEN + iphdrlen + (skb_vlan_tag_present(skb) ?
					       VLAN_HLEN : 0);

	/* Need space for new headers (invalidates ip_h ptr) */
	err = skb_cow_head(skb, min_headroom);
	if (unlikely(err))
		goto tx_error;

	if ((cp->flags & IP_VS_CONN_F_FWD_MASK) ==
	    IP_VS_CONN_F_NAT_IN_FULLNAT) {
		if ((pp->dnat_handler != NULL) && !pp->dnat_handler(skb, pp, cp, ipvsh))
			goto tx_error;
		ipv6_hdr(skb)->daddr = cp->daddr.in6;
	} else {
		if ((pp->fnat_in_handler != NULL) &&
		    !pp->fnat_in_handler(&skb, pp, cp, ipvsh))
			goto tx_error;
		ipv6_hdr(skb)->saddr = cp->laddr.in6;
		ipv6_hdr(skb)->daddr = cp->daddr.in6;
	}

	encap_info.peer_vtep = NULL;
	encap_info.vni = &cp->vni;

	mtu = (sysctl_dev_mtu(cp->ipvs) > (dst_mtu(dst) -
	       IP_VS_VXLAN_ENCAP_MAX_LEN)) ?
	      (dst_mtu(dst) - IP_VS_VXLAN_ENCAP_MAX_LEN) :
	      sysctl_dev_mtu(cp->ipvs);

	if (skb->len > mtu) {
		skb->ipvs_property = 1;
		skb_forward_csum(skb);
		/* Yet another hack: avoid icmp_send in ip_fragment */
		skb->ignore_df = 1;

		if (local_vtep.af == AF_INET) {
			ip_vs_ipv6_frag_over_vxlan(cp->ipvs, NULL, skb,
							   ip_vs_ip_finish_output2,
							   &encap_info);
		} else {
			ip_vs_ipv6_frag_over_vxlan(cp->ipvs, NULL, skb,
							   ip_vs_ip6_finish_output2,
							   &encap_info);
		}
		goto out;
	}

	vxlan_encapsulation(skb, &encap_info, NULL);
	goto out;

tx_error:
	kfree_skb(skb);
	LeaveFunction(10);
	return NF_STOLEN;

out:
	LeaveFunction(10);
	return NF_STOLEN;
}

int ip_vs_fnat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
		       struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
{
	struct rt6_info *rt;	/* Route to the other host */
	int mtu;
	struct frag_hdr *fhead;
	__u32 vni = 0;
	int local;

	EnterFunction(10);

	/* check if it is a connection of no-client-port */
	if (unlikely(cp->flags & IP_VS_CONN_F_NO_CPORT)) {
		__be16 _pt, *p;
		p = skb_header_pointer(skb, sizeof(struct ipv6hdr),
				       sizeof(_pt), &_pt);
		if (p == NULL)
			goto tx_error;
		ip_vs_conn_fill_cport(cp, *p);
		IP_VS_DBG(10, "filled cport=%d\n", ntohs(*p));
	}

	if (local_vtep.network_type != IP_VS_NETWORK_VLAN)
		vni = cp->vni.out_vni;

	if (vni)
		return ip_vs_fnat_xmit_v6_over_vxlan(skb, cp, pp, ipvsh);

	local = __ip_vs_get_out_rt_v6(cp->ipvs, skb, cp, &cp->daddr.in6, NULL,
			ipvsh, 0,
			IP_VS_RT_MODE_LOCAL |
			IP_VS_RT_MODE_NON_LOCAL |
			IP_VS_RT_MODE_RDR);
	if (local < 0)
		goto tx_error;

	rt = (struct rt6_info *) skb_dst(skb);
	if (unlikely(ipv6_hdr(skb)->nexthdr == IPPROTO_FRAGMENT)) {
		mtu = dst_mtu(&rt->dst);
		fhead = (struct frag_hdr *)skb_transport_header(skb);
		if (!gso_ok(skb, rt->dst.dev) && (skb->len > mtu) &&
		    ((fhead->frag_off) & htons(IP_DF))) {
			IP_VS_INC_ESTATS(ip_vs_esmib, XMIT_UNEXPECTED_MTU);
			icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
			IP_VS_DBG_RL_PKT(0, cp->af, pp, skb, 0,
					 "ip_vs_fnat_xmit_v6(): frag needed for");
			goto tx_error;
		}
	}

	ip_vs_save_xmit_info(skb, pp, cp);

	/* copy-on-write the packet before mangling it */
	if (skb_ensure_writable(skb, sizeof(struct ipv6hdr)))
		goto tx_error;

	if (skb_cow(skb, rt->dst.dev->hard_header_len))
		goto tx_error;

	/* mangle the packet */
	if (pp->fnat_in_handler && !pp->fnat_in_handler(&skb, pp, cp, ipvsh))
		goto tx_error;
	ipv6_hdr(skb)->saddr = cp->laddr.in6;
	ipv6_hdr(skb)->daddr = cp->daddr.in6;

	IP_VS_DBG_PKT(10, cp->af, pp, skb, 0, "After FNAT-IN");

	/* FIXME: when application helper enlarges the packet and the length
	   is larger than the MTU of outgoing device, there will be still
	   MTU problem. */

	/* Another hack: avoid icmp_send in ip_fragment */
	skb->ignore_df = 1;

	IP_VS_XMIT(PF_INET6, skb, rt, cp->ipvs->net);
	LeaveFunction(10);
	return NF_STOLEN;

tx_error:
	kfree_skb(skb);
	LeaveFunction(10);
	return NF_STOLEN;
}
#endif

/* When forwarding a packet, we must ensure that we've got enough headroom
 * for the encapsulation packet in the skb.  This also gives us an
 * opportunity to figure out what the payload_len, dsfield, ttl, and df
 * values should be, so that we won't need to look at the old ip header
 * again
 */
static struct sk_buff *
ip_vs_prepare_tunneled_skb(struct sk_buff *skb, int skb_af,
			   unsigned int max_headroom, __u8 *next_protocol,
			   __u32 *payload_len, __u8 *dsfield, __u8 *ttl,
			   __be16 *df)
{
	struct sk_buff *new_skb = NULL;
	struct iphdr *old_iph = NULL;
	__u8 old_dsfield;
#ifdef CONFIG_IP_VS_IPV6
	struct ipv6hdr *old_ipv6h = NULL;
#endif

	ip_vs_drop_early_demux_sk(skb);

	if (skb_headroom(skb) < max_headroom || skb_cloned(skb)) {
		new_skb = skb_realloc_headroom(skb, max_headroom);
		if (!new_skb)
			goto error;
		if (skb->sk)
			skb_set_owner_w(new_skb, skb->sk);
		consume_skb(skb);
		skb = new_skb;
	}

#ifdef CONFIG_IP_VS_IPV6
	if (skb_af == AF_INET6) {
		old_ipv6h = ipv6_hdr(skb);
		*next_protocol = IPPROTO_IPV6;
		if (payload_len)
			*payload_len =
				ntohs(old_ipv6h->payload_len) +
				sizeof(*old_ipv6h);
		old_dsfield = ipv6_get_dsfield(old_ipv6h);
		*ttl = old_ipv6h->hop_limit;
		if (df)
			*df = 0;
	} else
#endif
	{
		old_iph = ip_hdr(skb);
		/* Copy DF, reset fragment offset and MF */
		if (df)
			*df = (old_iph->frag_off & htons(IP_DF));
		*next_protocol = IPPROTO_IPIP;

		/* fix old IP header checksum */
		ip_send_check(old_iph);
		old_dsfield = ipv4_get_dsfield(old_iph);
		*ttl = old_iph->ttl;
		if (payload_len)
			*payload_len = ntohs(old_iph->tot_len);
	}

	/* Implement full-functionality option for ECN encapsulation */
	*dsfield = INET_ECN_encapsulate(old_dsfield, old_dsfield);

	return skb;
error:
	kfree_skb(skb);
	return ERR_PTR(-ENOMEM);
}

static inline int __tun_gso_type_mask(int encaps_af, int orig_af)
{
	switch (encaps_af) {
	case AF_INET:
		return SKB_GSO_IPXIP4;
	case AF_INET6:
		return SKB_GSO_IPXIP6;
	default:
		return 0;
	}
}

static int
ipvs_gue_encap(struct net *net, struct sk_buff *skb,
	       struct ip_vs_conn *cp, __u8 *next_protocol)
{
	__be16 dport;
	__be16 sport = udp_flow_src_port(net, skb, 0, 0, false);
	struct udphdr  *udph;	/* Our new UDP header */
	struct guehdr  *gueh;	/* Our new GUE header */
	size_t hdrlen, optlen = 0;
	void *data;
	bool need_priv = false;

	if ((cp->dest->tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_REMCSUM) &&
	    skb->ip_summed == CHECKSUM_PARTIAL) {
		optlen += GUE_PLEN_REMCSUM + GUE_LEN_PRIV;
		need_priv = true;
	}

	hdrlen = sizeof(struct guehdr) + optlen;

	skb_push(skb, hdrlen);

	gueh = (struct guehdr *)skb->data;

	gueh->control = 0;
	gueh->version = 0;
	gueh->hlen = optlen >> 2;
	gueh->flags = 0;
	gueh->proto_ctype = *next_protocol;

	data = &gueh[1];

	if (need_priv) {
		__be32 *flags = data;
		u16 csum_start = skb_checksum_start_offset(skb);
		__be16 *pd;

		gueh->flags |= GUE_FLAG_PRIV;
		*flags = 0;
		data += GUE_LEN_PRIV;

		if (csum_start < hdrlen)
			return -EINVAL;

		csum_start -= hdrlen;
		pd = data;
		pd[0] = htons(csum_start);
		pd[1] = htons(csum_start + skb->csum_offset);

		if (!skb_is_gso(skb)) {
			skb->ip_summed = CHECKSUM_NONE;
			skb->encapsulation = 0;
		}

		*flags |= GUE_PFLAG_REMCSUM;
		data += GUE_PLEN_REMCSUM;
	}

	skb_push(skb, sizeof(struct udphdr));
	skb_reset_transport_header(skb);

	udph = udp_hdr(skb);

	dport = cp->dest->tun_port;
	udph->dest = dport;
	udph->source = sport;
	udph->len = htons(skb->len);
	udph->check = 0;

	*next_protocol = IPPROTO_UDP;

	return 0;
}

static void
ipvs_gre_encap(struct net *net, struct sk_buff *skb,
	       struct ip_vs_conn *cp, __u8 *next_protocol)
{
	__be16 proto = *next_protocol == IPPROTO_IPIP ?
				htons(ETH_P_IP) : htons(ETH_P_IPV6);
	__be16 tflags = 0;
	size_t hdrlen;

	if (cp->dest->tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_CSUM)
		tflags |= TUNNEL_CSUM;

	hdrlen = gre_calc_hlen(tflags);
	gre_build_header(skb, hdrlen, tflags, proto, 0, 0);

	*next_protocol = IPPROTO_GRE;
}

/*
 *   IP Tunneling transmitter
 *
 *   This function encapsulates the packet in a new IP packet, its
 *   destination will be set to cp->daddr. Most code of this function
 *   is taken from ipip.c.
 *
 *   It is used in VS/TUN cluster. The load balancer selects a real
 *   server from a cluster based on a scheduling algorithm,
 *   encapsulates the request packet and forwards it to the selected
 *   server. For example, all real servers are configured with
 *   "ifconfig tunl0 <Virtual IP Address> up". When the server receives
 *   the encapsulated packet, it will decapsulate the packet, processe
 *   the request and return the response packets directly to the client
 *   without passing the load balancer. This can greatly increase the
 *   scalability of virtual server.
 *
 *   Used for ANY protocol
 */
int
ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
		  struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
{
	struct netns_ipvs *ipvs = cp->ipvs;
	struct net *net = ipvs->net;
	struct rtable *rt;			/* Route to the other host */
	__be32 saddr;				/* Source for tunnel */
	struct net_device *tdev;		/* Device to other host */
	__u8 next_protocol = 0;
	__u8 dsfield = 0;
	__u8 ttl = 0;
	__be16 df = 0;
	__be16 *dfp = NULL;
	struct iphdr  *iph;			/* Our new IP header */
	unsigned int max_headroom;		/* The extra header space needed */
	int ret, local;
	int tun_type, gso_type;
	int tun_flags;

	EnterFunction(10);

	local = __ip_vs_get_out_rt(ipvs, skb, cp, cp->daddr.ip,
				   IP_VS_RT_MODE_LOCAL |
				   IP_VS_RT_MODE_NON_LOCAL |
				   IP_VS_RT_MODE_CONNECT |
				   IP_VS_RT_MODE_TUNNEL, &saddr, ipvsh);
	if (local < 0)
		goto tx_error;
	if (local)
		return ip_vs_send_or_cont(NFPROTO_IPV4, skb, cp, 1);

	rt = skb_rtable(skb);
	tdev = rt->dst.dev;

	/*
	 * Okay, now see if we can stuff it in the buffer as-is.
	 */
	max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(struct iphdr);

	tun_type = cp->dest->tun_type;
	tun_flags = cp->dest->tun_flags;

	if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GUE) {
		size_t gue_hdrlen, gue_optlen = 0;

		if ((tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_REMCSUM) &&
		    skb->ip_summed == CHECKSUM_PARTIAL) {
			gue_optlen += GUE_PLEN_REMCSUM + GUE_LEN_PRIV;
		}
		gue_hdrlen = sizeof(struct guehdr) + gue_optlen;

		max_headroom += sizeof(struct udphdr) + gue_hdrlen;
	} else if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE) {
		size_t gre_hdrlen;
		__be16 tflags = 0;

		if (tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_CSUM)
			tflags |= TUNNEL_CSUM;
		gre_hdrlen = gre_calc_hlen(tflags);

		max_headroom += gre_hdrlen;
	}

	/* We only care about the df field if sysctl_pmtu_disc(ipvs) is set */
	dfp = sysctl_pmtu_disc(ipvs) ? &df : NULL;
	skb = ip_vs_prepare_tunneled_skb(skb, cp->af, max_headroom,
					 &next_protocol, NULL, &dsfield,
					 &ttl, dfp);
	if (IS_ERR(skb))
		goto tx_error;

	gso_type = __tun_gso_type_mask(AF_INET, cp->af);
	if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GUE) {
		if ((tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_CSUM) ||
		    (tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_REMCSUM))
			gso_type |= SKB_GSO_UDP_TUNNEL_CSUM;
		else
			gso_type |= SKB_GSO_UDP_TUNNEL;
		if ((tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_REMCSUM) &&
		    skb->ip_summed == CHECKSUM_PARTIAL) {
			gso_type |= SKB_GSO_TUNNEL_REMCSUM;
		}
	} else if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE) {
		if (tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_CSUM)
			gso_type |= SKB_GSO_GRE_CSUM;
		else
			gso_type |= SKB_GSO_GRE;
	}

	if (iptunnel_handle_offloads(skb, gso_type))
		goto tx_error;

	skb->transport_header = skb->network_header;

	skb_set_inner_ipproto(skb, next_protocol);

	if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GUE) {
		bool check = false;

		if (ipvs_gue_encap(net, skb, cp, &next_protocol))
			goto tx_error;

		if ((tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_CSUM) ||
		    (tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_REMCSUM))
			check = true;

		udp_set_csum(!check, skb, saddr, cp->daddr.ip, skb->len);
	} else if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE)
		ipvs_gre_encap(net, skb, cp, &next_protocol);

	skb_push(skb, sizeof(struct iphdr));
	skb_reset_network_header(skb);
	memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));

	/*
	 *	Push down and install the IPIP header.
	 */
	iph			=	ip_hdr(skb);
	iph->version		=	4;
	iph->ihl		=	sizeof(struct iphdr)>>2;
	iph->frag_off		=	df;
	iph->protocol		=	next_protocol;
	iph->tos		=	dsfield;
	iph->daddr		=	cp->daddr.ip;
	iph->saddr		=	saddr;
	iph->ttl		=	ttl;
	ip_select_ident(net, skb, NULL);

	/* Another hack: avoid icmp_send in ip_fragment */
	skb->ignore_df = 1;

	ret = ip_vs_tunnel_xmit_prepare(skb, cp);
	if (ret == NF_ACCEPT)
		ip_local_out(net, skb->sk, skb);
	else if (ret == NF_DROP)
		kfree_skb(skb);

	LeaveFunction(10);

	return NF_STOLEN;

  tx_error:
	if (!IS_ERR(skb))
		kfree_skb(skb);
	LeaveFunction(10);
	return NF_STOLEN;
}

#ifdef CONFIG_IP_VS_IPV6
int
ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
		     struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
{
	struct netns_ipvs *ipvs = cp->ipvs;
	struct net *net = ipvs->net;
	struct rt6_info *rt;		/* Route to the other host */
	struct in6_addr saddr;		/* Source for tunnel */
	struct net_device *tdev;	/* Device to other host */
	__u8 next_protocol = 0;
	__u32 payload_len = 0;
	__u8 dsfield = 0;
	__u8 ttl = 0;
	struct ipv6hdr  *iph;		/* Our new IP header */
	unsigned int max_headroom;	/* The extra header space needed */
	int ret, local;
	int tun_type, gso_type;
	int tun_flags;

	EnterFunction(10);

	local = __ip_vs_get_out_rt_v6(ipvs, skb, cp,
				      &cp->daddr.in6,
				      &saddr, ipvsh, 1,
				      IP_VS_RT_MODE_LOCAL |
				      IP_VS_RT_MODE_NON_LOCAL |
				      IP_VS_RT_MODE_TUNNEL);
	if (local < 0)
		goto tx_error;
	if (local)
		return ip_vs_send_or_cont(NFPROTO_IPV6, skb, cp, 1);

	rt = (struct rt6_info *) skb_dst(skb);
	tdev = rt->dst.dev;

	/*
	 * Okay, now see if we can stuff it in the buffer as-is.
	 */
	max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(struct ipv6hdr);

	tun_type = cp->dest->tun_type;
	tun_flags = cp->dest->tun_flags;

	if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GUE) {
		size_t gue_hdrlen, gue_optlen = 0;

		if ((tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_REMCSUM) &&
		    skb->ip_summed == CHECKSUM_PARTIAL) {
			gue_optlen += GUE_PLEN_REMCSUM + GUE_LEN_PRIV;
		}
		gue_hdrlen = sizeof(struct guehdr) + gue_optlen;

		max_headroom += sizeof(struct udphdr) + gue_hdrlen;
	} else if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE) {
		size_t gre_hdrlen;
		__be16 tflags = 0;

		if (tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_CSUM)
			tflags |= TUNNEL_CSUM;
		gre_hdrlen = gre_calc_hlen(tflags);

		max_headroom += gre_hdrlen;
	}

	skb = ip_vs_prepare_tunneled_skb(skb, cp->af, max_headroom,
					 &next_protocol, &payload_len,
					 &dsfield, &ttl, NULL);
	if (IS_ERR(skb))
		goto tx_error;

	gso_type = __tun_gso_type_mask(AF_INET6, cp->af);
	if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GUE) {
		if ((tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_CSUM) ||
		    (tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_REMCSUM))
			gso_type |= SKB_GSO_UDP_TUNNEL_CSUM;
		else
			gso_type |= SKB_GSO_UDP_TUNNEL;
		if ((tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_REMCSUM) &&
		    skb->ip_summed == CHECKSUM_PARTIAL) {
			gso_type |= SKB_GSO_TUNNEL_REMCSUM;
		}
	} else if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE) {
		if (tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_CSUM)
			gso_type |= SKB_GSO_GRE_CSUM;
		else
			gso_type |= SKB_GSO_GRE;
	}

	if (iptunnel_handle_offloads(skb, gso_type))
		goto tx_error;

	skb->transport_header = skb->network_header;

	skb_set_inner_ipproto(skb, next_protocol);

	if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GUE) {
		bool check = false;

		if (ipvs_gue_encap(net, skb, cp, &next_protocol))
			goto tx_error;

		if ((tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_CSUM) ||
		    (tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_REMCSUM))
			check = true;

		udp6_set_csum(!check, skb, &saddr, &cp->daddr.in6, skb->len);
	} else if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE)
		ipvs_gre_encap(net, skb, cp, &next_protocol);

	skb_push(skb, sizeof(struct ipv6hdr));
	skb_reset_network_header(skb);
	memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));

	/*
	 *	Push down and install the IPIP header.
	 */
	iph			=	ipv6_hdr(skb);
	iph->version		=	6;
	iph->nexthdr		=	next_protocol;
	iph->payload_len	=	htons(payload_len);
	memset(&iph->flow_lbl, 0, sizeof(iph->flow_lbl));
	ipv6_change_dsfield(iph, 0, dsfield);
	iph->daddr = cp->daddr.in6;
	iph->saddr = saddr;
	iph->hop_limit		=	ttl;

	/* Another hack: avoid icmp_send in ip_fragment */
	skb->ignore_df = 1;

	ret = ip_vs_tunnel_xmit_prepare(skb, cp);
	if (ret == NF_ACCEPT)
		ip6_local_out(net, skb->sk, skb);
	else if (ret == NF_DROP)
		kfree_skb(skb);

	LeaveFunction(10);

	return NF_STOLEN;

tx_error:
	if (!IS_ERR(skb))
		kfree_skb(skb);
	LeaveFunction(10);
	return NF_STOLEN;
}
#endif


/*
 *      Direct Routing transmitter
 *      Used for ANY protocol
 */
int
ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
	      struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
{
	int local;

	EnterFunction(10);

	local = __ip_vs_get_out_rt(cp->ipvs, skb, cp, cp->daddr.ip,
				   IP_VS_RT_MODE_LOCAL |
				   IP_VS_RT_MODE_NON_LOCAL |
				   IP_VS_RT_MODE_KNOWN_NH, NULL, ipvsh);
	if (local < 0)
		goto tx_error;
	if (local)
		return ip_vs_send_or_cont(NFPROTO_IPV4, skb, cp, 1);

	ip_send_check(ip_hdr(skb));

	/* Another hack: avoid icmp_send in ip_fragment */
	skb->ignore_df = 1;

	ip_vs_send_or_cont(NFPROTO_IPV4, skb, cp, 0);

	LeaveFunction(10);
	return NF_STOLEN;

  tx_error:
	kfree_skb(skb);
	LeaveFunction(10);
	return NF_STOLEN;
}

#ifdef CONFIG_IP_VS_IPV6
int
ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
		 struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
{
	int local;

	EnterFunction(10);

	local = __ip_vs_get_out_rt_v6(cp->ipvs, skb, cp,
				      &cp->daddr.in6,
				      NULL, ipvsh, 0,
				      IP_VS_RT_MODE_LOCAL |
				      IP_VS_RT_MODE_NON_LOCAL |
				      IP_VS_RT_MODE_KNOWN_NH);
	if (local < 0)
		goto tx_error;
	if (local)
		return ip_vs_send_or_cont(NFPROTO_IPV6, skb, cp, 1);

	/* Another hack: avoid icmp_send in ip_fragment */
	skb->ignore_df = 1;

	ip_vs_send_or_cont(NFPROTO_IPV6, skb, cp, 0);

	LeaveFunction(10);
	return NF_STOLEN;

tx_error:
	kfree_skb(skb);
	LeaveFunction(10);
	return NF_STOLEN;
}
#endif


/*
 *	ICMP packet transmitter
 *	called by the ip_vs_in_icmp
 */
int
ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
		struct ip_vs_protocol *pp, int offset, unsigned int hooknum,
		struct ip_vs_iphdr *iph)
{
	struct rtable	*rt;	/* Route to the other host */
	int rc;
	int local;
	int rt_mode, was_input;
	__u32 vni = 0;
	struct ipvs_vxlan_encapsulation_info encap_info;

	EnterFunction(10);

	if (local_vtep.network_type != IP_VS_NETWORK_VLAN)
		vni = cp->vni.out_vni;

	/* The ICMP packet for VS/TUN, VS/DR and LOCALNODE will be
	   forwarded directly here, because there is no need to
	   translate address/port back */
	if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) {
		if (cp->packet_xmit)
			rc = cp->packet_xmit(skb, cp, pp, iph);
		else
			rc = NF_ACCEPT;
		/* do not touch skb anymore */
		atomic_inc(&cp->in_pkts);
		goto out;
	}

	/*
	 * mangle and send the packet here (only for VS/NAT)
	 */
	was_input = rt_is_input_route(skb_rtable(skb));

	/* LOCALNODE from FORWARD hook is not supported */
	rt_mode = (hooknum != NF_INET_FORWARD) ?
		  IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL |
		  IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL;
	local = __ip_vs_get_out_rt(cp->ipvs, skb, cp, cp->daddr.ip, rt_mode,
				   NULL, iph);
	if (local < 0)
		goto tx_error;
	rt = skb_rtable(skb);

	/*
	 * Avoid duplicate tuple in reply direction for NAT traffic
	 * to local address when connection is sync-ed
	 */
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
	if (cp->flags & IP_VS_CONN_F_SYNC && local) {
		enum ip_conntrack_info ctinfo;
		struct nf_conn *ct = nf_ct_get(skb, &ctinfo);

		if (ct) {
			IP_VS_DBG(10, "%s(): "
				  "stopping DNAT to local address %pI4\n",
				  __func__, &cp->daddr.ip);
			goto tx_error;
		}
	}
#endif

	/* From world but DNAT to loopback address? */
	if (local && ipv4_is_loopback(cp->daddr.ip) && was_input) {
		IP_VS_DBG(1, "%s(): "
			  "stopping DNAT to loopback %pI4\n",
			  __func__, &cp->daddr.ip);
		goto tx_error;
	}

	/* copy-on-write the packet before mangling it */
	if (skb_ensure_writable(skb, offset))
		goto tx_error;

	if (skb_cow(skb, rt->dst.dev->hard_header_len))
		goto tx_error;

	ip_vs_nat_icmp(skb, pp, cp, 0);

	/* Another hack: avoid icmp_send in ip_fragment */
	skb->ignore_df = 1;

	if (vni) {
		if (skb->len > skb->dev->mtu) {
			skb->ipvs_property = 1;
			skb_forward_csum(skb);
			/* Another hack: avoid icmp_send in ip_fragment */
			skb->ignore_df = 1;
			ip_vs_ip_fragment(cp->ipvs, NULL, skb,
				ip_vs_ip_finish_output2, cp);
			rc = NF_STOLEN;
			goto out;
		} else {
			encap_info.peer_vtep = NULL;
			encap_info.vni = &cp->vni;
			vxlan_encapsulation(skb, &encap_info, NULL);
			rc = NF_STOLEN;
			goto out;
		}
	} else {
		rc = ip_vs_nat_send_or_cont(NFPROTO_IPV4, skb, cp, local);
	}
	goto out;

  tx_error:
	kfree_skb(skb);
	rc = NF_STOLEN;
  out:
	LeaveFunction(10);
	return rc;
}

#ifdef CONFIG_IP_VS_IPV6
int
ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
		struct ip_vs_protocol *pp, int offset, unsigned int hooknum,
		struct ip_vs_iphdr *ipvsh)
{
	struct rt6_info	*rt;	/* Route to the other host */
	int rc;
	int local;
	int rt_mode;

	EnterFunction(10);

	/* The ICMP packet for VS/TUN, VS/DR and LOCALNODE will be
	   forwarded directly here, because there is no need to
	   translate address/port back */
	if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) {
		if (cp->packet_xmit)
			rc = cp->packet_xmit(skb, cp, pp, ipvsh);
		else
			rc = NF_ACCEPT;
		/* do not touch skb anymore */
		atomic_inc(&cp->in_pkts);
		goto out;
	}

	if (cp->vni.out_vni) {
		/* go vni and add outer hdr */
		return ip_vs_fnat_xmit_v6_over_vxlan(skb, cp, pp, ipvsh);
	}

	/*
	 * mangle and send the packet here (only for VS/NAT)
	 */

	/* LOCALNODE from FORWARD hook is not supported */
	rt_mode = (hooknum != NF_INET_FORWARD) ?
		  IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL |
		  IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL;
	local = __ip_vs_get_out_rt_v6(cp->ipvs, skb, cp,
				      &cp->daddr.in6, NULL, ipvsh, 0, rt_mode);
	if (local < 0)
		goto tx_error;
	rt = (struct rt6_info *) skb_dst(skb);
	/*
	 * Avoid duplicate tuple in reply direction for NAT traffic
	 * to local address when connection is sync-ed
	 */
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
	if (cp->flags & IP_VS_CONN_F_SYNC && local) {
		enum ip_conntrack_info ctinfo;
		struct nf_conn *ct = nf_ct_get(skb, &ctinfo);

		if (ct) {
			IP_VS_DBG(10, "%s(): "
				  "stopping DNAT to local address %pI6\n",
				  __func__, &cp->daddr.in6);
			goto tx_error;
		}
	}
#endif

	/* From world but DNAT to loopback address? */
	if (local && skb->dev && !(skb->dev->flags & IFF_LOOPBACK) &&
	    ipv6_addr_type(&cp->daddr.in6) & IPV6_ADDR_LOOPBACK) {
		IP_VS_DBG(1, "%s(): "
			  "stopping DNAT to loopback %pI6\n",
			  __func__, &cp->daddr.in6);
		goto tx_error;
	}

	/* copy-on-write the packet before mangling it */
	if (skb_ensure_writable(skb, offset))
		goto tx_error;

	if (skb_cow(skb, rt->dst.dev->hard_header_len))
		goto tx_error;

	ip_vs_nat_icmp_v6(skb, pp, cp, 0);

	/* Another hack: avoid icmp_send in ip_fragment */
	skb->ignore_df = 1;

	rc = ip_vs_nat_send_or_cont(NFPROTO_IPV6, skb, cp, local);
	goto out;

tx_error:
	kfree_skb(skb);
	rc = NF_STOLEN;
out:
	LeaveFunction(10);
	return rc;
}
#endif
